4185 add new cryptographic checksums to ZFS: SHA-512, Skein, Edon-R

Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Garrett D'Amore <garrett@damore.org>
Author: Matthew Ahrens <mahrens@delphix.com>

illumos/illumos-gate@45818ee124
This commit is contained in:
Alexander Motin 2015-10-14 11:12:47 +00:00
parent 255a5f2cac
commit 456aaec66d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor-sys/illumos/dist/; revision=289310
40 changed files with 5150 additions and 78 deletions

729
common/crypto/edonr/edonr.c Normal file
View File

@ -0,0 +1,729 @@
/*
* IDI,NTNU
*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright (C) 2009, 2010, Jorn Amundsen <jorn.amundsen@ntnu.no>
* Tweaked Edon-R implementation for SUPERCOP, based on NIST API.
*
* $Id: edonr.c 517 2013-02-17 20:34:39Z joern $
*/
/*
* Portions copyright (c) 2013, Saso Kiselkov, All rights reserved
*/
/* determine where we can get bcopy/bzero declarations */
#ifdef _KERNEL
#include <sys/systm.h>
#else
#include <strings.h>
#endif
#include <sys/edonr.h>
#include <sys/debug.h>
/* big endian support, provides no-op's if run on little endian hosts */
#include "edonr_byteorder.h"
#define hashState224(x) ((x)->pipe->p256)
#define hashState256(x) ((x)->pipe->p256)
#define hashState384(x) ((x)->pipe->p512)
#define hashState512(x) ((x)->pipe->p512)
/* shift and rotate shortcuts */
#define shl(x, n) ((x) << n)
#define shr(x, n) ((x) >> n)
#define rotl32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define rotr32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
#define rotl64(x, n) (((x) << (n)) | ((x) >> (64 - (n))))
#define rotr64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
#if !defined(__C99_RESTRICT)
#define restrict /* restrict */
#endif
#define EDONR_VALID_HASHBITLEN(x) \
((x) == 512 || (x) == 384 || (x) == 256 || (x) == 224)
/* EdonR224 initial double chaining pipe */
static const uint32_t i224p2[16] = {
0x00010203ul, 0x04050607ul, 0x08090a0bul, 0x0c0d0e0ful,
0x10111213ul, 0x14151617ul, 0x18191a1bul, 0x1c1d1e1ful,
0x20212223ul, 0x24252627ul, 0x28292a2bul, 0x2c2d2e2ful,
0x30313233ul, 0x34353637ul, 0x38393a3bul, 0x3c3d3e3ful,
};
/* EdonR256 initial double chaining pipe */
static const uint32_t i256p2[16] = {
0x40414243ul, 0x44454647ul, 0x48494a4bul, 0x4c4d4e4ful,
0x50515253ul, 0x54555657ul, 0x58595a5bul, 0x5c5d5e5ful,
0x60616263ul, 0x64656667ul, 0x68696a6bul, 0x6c6d6e6ful,
0x70717273ul, 0x74757677ul, 0x78797a7bul, 0x7c7d7e7ful,
};
/* EdonR384 initial double chaining pipe */
static const uint64_t i384p2[16] = {
0x0001020304050607ull, 0x08090a0b0c0d0e0full,
0x1011121314151617ull, 0x18191a1b1c1d1e1full,
0x2021222324252627ull, 0x28292a2b2c2d2e2full,
0x3031323334353637ull, 0x38393a3b3c3d3e3full,
0x4041424344454647ull, 0x48494a4b4c4d4e4full,
0x5051525354555657ull, 0x58595a5b5c5d5e5full,
0x6061626364656667ull, 0x68696a6b6c6d6e6full,
0x7071727374757677ull, 0x78797a7b7c7d7e7full
};
/* EdonR512 initial double chaining pipe */
static const uint64_t i512p2[16] = {
0x8081828384858687ull, 0x88898a8b8c8d8e8full,
0x9091929394959697ull, 0x98999a9b9c9d9e9full,
0xa0a1a2a3a4a5a6a7ull, 0xa8a9aaabacadaeafull,
0xb0b1b2b3b4b5b6b7ull, 0xb8b9babbbcbdbebfull,
0xc0c1c2c3c4c5c6c7ull, 0xc8c9cacbcccdcecfull,
0xd0d1d2d3d4d5d6d7ull, 0xd8d9dadbdcdddedfull,
0xe0e1e2e3e4e5e6e7ull, 0xe8e9eaebecedeeefull,
0xf0f1f2f3f4f5f6f7ull, 0xf8f9fafbfcfdfeffull
};
/*
* First Latin Square
* 0 7 1 3 2 4 6 5
* 4 1 7 6 3 0 5 2
* 7 0 4 2 5 3 1 6
* 1 4 0 5 6 2 7 3
* 2 3 6 7 1 5 0 4
* 5 2 3 1 7 6 4 0
* 3 6 5 0 4 7 2 1
* 6 5 2 4 0 1 3 7
*/
#define LS1_256(c, x0, x1, x2, x3, x4, x5, x6, x7) \
{ \
uint32_t x04, x17, x23, x56, x07, x26; \
x04 = x0+x4, x17 = x1+x7, x07 = x04+x17; \
s0 = c + x07 + x2; \
s1 = rotl32(x07 + x3, 4); \
s2 = rotl32(x07 + x6, 8); \
x23 = x2 + x3; \
s5 = rotl32(x04 + x23 + x5, 22); \
x56 = x5 + x6; \
s6 = rotl32(x17 + x56 + x0, 24); \
x26 = x23+x56; \
s3 = rotl32(x26 + x7, 13); \
s4 = rotl32(x26 + x1, 17); \
s7 = rotl32(x26 + x4, 29); \
}
#define LS1_512(c, x0, x1, x2, x3, x4, x5, x6, x7) \
{ \
uint64_t x04, x17, x23, x56, x07, x26; \
x04 = x0+x4, x17 = x1+x7, x07 = x04+x17; \
s0 = c + x07 + x2; \
s1 = rotl64(x07 + x3, 5); \
s2 = rotl64(x07 + x6, 15); \
x23 = x2 + x3; \
s5 = rotl64(x04 + x23 + x5, 40); \
x56 = x5 + x6; \
s6 = rotl64(x17 + x56 + x0, 50); \
x26 = x23+x56; \
s3 = rotl64(x26 + x7, 22); \
s4 = rotl64(x26 + x1, 31); \
s7 = rotl64(x26 + x4, 59); \
}
/*
* Second Orthogonal Latin Square
* 0 4 2 3 1 6 5 7
* 7 6 3 2 5 4 1 0
* 5 3 1 6 0 2 7 4
* 1 0 5 4 3 7 2 6
* 2 1 0 7 4 5 6 3
* 3 5 7 0 6 1 4 2
* 4 7 6 1 2 0 3 5
* 6 2 4 5 7 3 0 1
*/
#define LS2_256(c, y0, y1, y2, y3, y4, y5, y6, y7) \
{ \
uint32_t y01, y25, y34, y67, y04, y05, y27, y37; \
y01 = y0+y1, y25 = y2+y5, y05 = y01+y25; \
t0 = ~c + y05 + y7; \
t2 = rotl32(y05 + y3, 9); \
y34 = y3+y4, y04 = y01+y34; \
t1 = rotl32(y04 + y6, 5); \
t4 = rotl32(y04 + y5, 15); \
y67 = y6+y7, y37 = y34+y67; \
t3 = rotl32(y37 + y2, 11); \
t7 = rotl32(y37 + y0, 27); \
y27 = y25+y67; \
t5 = rotl32(y27 + y4, 20); \
t6 = rotl32(y27 + y1, 25); \
}
#define LS2_512(c, y0, y1, y2, y3, y4, y5, y6, y7) \
{ \
uint64_t y01, y25, y34, y67, y04, y05, y27, y37; \
y01 = y0+y1, y25 = y2+y5, y05 = y01+y25; \
t0 = ~c + y05 + y7; \
t2 = rotl64(y05 + y3, 19); \
y34 = y3+y4, y04 = y01+y34; \
t1 = rotl64(y04 + y6, 10); \
t4 = rotl64(y04 + y5, 36); \
y67 = y6+y7, y37 = y34+y67; \
t3 = rotl64(y37 + y2, 29); \
t7 = rotl64(y37 + y0, 55); \
y27 = y25+y67; \
t5 = rotl64(y27 + y4, 44); \
t6 = rotl64(y27 + y1, 48); \
}
#define quasi_exform256(r0, r1, r2, r3, r4, r5, r6, r7) \
{ \
uint32_t s04, s17, s23, s56, t01, t25, t34, t67; \
s04 = s0 ^ s4, t01 = t0 ^ t1; \
r0 = (s04 ^ s1) + (t01 ^ t5); \
t67 = t6 ^ t7; \
r1 = (s04 ^ s7) + (t2 ^ t67); \
s23 = s2 ^ s3; \
r7 = (s23 ^ s5) + (t4 ^ t67); \
t34 = t3 ^ t4; \
r3 = (s23 ^ s4) + (t0 ^ t34); \
s56 = s5 ^ s6; \
r5 = (s3 ^ s56) + (t34 ^ t6); \
t25 = t2 ^ t5; \
r6 = (s2 ^ s56) + (t25 ^ t7); \
s17 = s1 ^ s7; \
r4 = (s0 ^ s17) + (t1 ^ t25); \
r2 = (s17 ^ s6) + (t01 ^ t3); \
}
#define quasi_exform512(r0, r1, r2, r3, r4, r5, r6, r7) \
{ \
uint64_t s04, s17, s23, s56, t01, t25, t34, t67; \
s04 = s0 ^ s4, t01 = t0 ^ t1; \
r0 = (s04 ^ s1) + (t01 ^ t5); \
t67 = t6 ^ t7; \
r1 = (s04 ^ s7) + (t2 ^ t67); \
s23 = s2 ^ s3; \
r7 = (s23 ^ s5) + (t4 ^ t67); \
t34 = t3 ^ t4; \
r3 = (s23 ^ s4) + (t0 ^ t34); \
s56 = s5 ^ s6; \
r5 = (s3 ^ s56) + (t34 ^ t6); \
t25 = t2 ^ t5; \
r6 = (s2 ^ s56) + (t25 ^ t7); \
s17 = s1 ^ s7; \
r4 = (s0 ^ s17) + (t1 ^ t25); \
r2 = (s17 ^ s6) + (t01 ^ t3); \
}
static size_t
Q256(size_t bitlen, const uint32_t *data, uint32_t *restrict p)
{
size_t bl;
for (bl = bitlen; bl >= EdonR256_BLOCK_BITSIZE;
bl -= EdonR256_BLOCK_BITSIZE, data += 16) {
uint32_t s0, s1, s2, s3, s4, s5, s6, s7, t0, t1, t2, t3, t4,
t5, t6, t7;
uint32_t p0, p1, p2, p3, p4, p5, p6, p7, q0, q1, q2, q3, q4,
q5, q6, q7;
const uint32_t defix = 0xaaaaaaaa;
#if defined(MACHINE_IS_BIG_ENDIAN)
uint32_t swp0, swp1, swp2, swp3, swp4, swp5, swp6, swp7, swp8,
swp9, swp10, swp11, swp12, swp13, swp14, swp15;
#define d(j) swp ## j
#define s32(j) ld_swap32((uint32_t *)data + j, swp ## j)
#else
#define d(j) data[j]
#endif
/* First row of quasigroup e-transformations */
#if defined(MACHINE_IS_BIG_ENDIAN)
s32(8);
s32(9);
s32(10);
s32(11);
s32(12);
s32(13);
s32(14);
s32(15);
#endif
LS1_256(defix, d(15), d(14), d(13), d(12), d(11), d(10), d(9),
d(8));
#if defined(MACHINE_IS_BIG_ENDIAN)
s32(0);
s32(1);
s32(2);
s32(3);
s32(4);
s32(5);
s32(6);
s32(7);
#undef s32
#endif
LS2_256(defix, d(0), d(1), d(2), d(3), d(4), d(5), d(6), d(7));
quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_256(defix, d(8), d(9), d(10), d(11), d(12), d(13), d(14),
d(15));
quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7);
/* Second row of quasigroup e-transformations */
LS1_256(defix, p[8], p[9], p[10], p[11], p[12], p[13], p[14],
p[15]);
LS2_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_256(defix, q0, q1, q2, q3, q4, q5, q6, q7);
quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7);
/* Third row of quasigroup e-transformations */
LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_256(defix, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_256(defix, q0, q1, q2, q3, q4, q5, q6, q7);
LS2_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7);
/* Fourth row of quasigroup e-transformations */
LS1_256(defix, d(7), d(6), d(5), d(4), d(3), d(2), d(1), d(0));
LS2_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
quasi_exform256(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_256(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_256(defix, q0, q1, q2, q3, q4, q5, q6, q7);
quasi_exform256(q0, q1, q2, q3, q4, q5, q6, q7);
/* Edon-R tweak on the original SHA-3 Edon-R submission. */
p[0] ^= d(8) ^ p0;
p[1] ^= d(9) ^ p1;
p[2] ^= d(10) ^ p2;
p[3] ^= d(11) ^ p3;
p[4] ^= d(12) ^ p4;
p[5] ^= d(13) ^ p5;
p[6] ^= d(14) ^ p6;
p[7] ^= d(15) ^ p7;
p[8] ^= d(0) ^ q0;
p[9] ^= d(1) ^ q1;
p[10] ^= d(2) ^ q2;
p[11] ^= d(3) ^ q3;
p[12] ^= d(4) ^ q4;
p[13] ^= d(5) ^ q5;
p[14] ^= d(6) ^ q6;
p[15] ^= d(7) ^ q7;
}
#undef d
return (bitlen - bl);
}
#if defined(__IBMC__) && defined(_AIX) && defined(__64BIT__)
static inline size_t
#else
static size_t
#endif
Q512(size_t bitlen, const uint64_t *data, uint64_t *restrict p)
{
size_t bl;
for (bl = bitlen; bl >= EdonR512_BLOCK_BITSIZE;
bl -= EdonR512_BLOCK_BITSIZE, data += 16) {
uint64_t s0, s1, s2, s3, s4, s5, s6, s7, t0, t1, t2, t3, t4,
t5, t6, t7;
uint64_t p0, p1, p2, p3, p4, p5, p6, p7, q0, q1, q2, q3, q4,
q5, q6, q7;
const uint64_t defix = 0xaaaaaaaaaaaaaaaaull;
#if defined(MACHINE_IS_BIG_ENDIAN)
uint64_t swp0, swp1, swp2, swp3, swp4, swp5, swp6, swp7, swp8,
swp9, swp10, swp11, swp12, swp13, swp14, swp15;
#define d(j) swp##j
#define s64(j) ld_swap64((uint64_t *)data+j, swp##j)
#else
#define d(j) data[j]
#endif
/* First row of quasigroup e-transformations */
#if defined(MACHINE_IS_BIG_ENDIAN)
s64(8);
s64(9);
s64(10);
s64(11);
s64(12);
s64(13);
s64(14);
s64(15);
#endif
LS1_512(defix, d(15), d(14), d(13), d(12), d(11), d(10), d(9),
d(8));
#if defined(MACHINE_IS_BIG_ENDIAN)
s64(0);
s64(1);
s64(2);
s64(3);
s64(4);
s64(5);
s64(6);
s64(7);
#undef s64
#endif
LS2_512(defix, d(0), d(1), d(2), d(3), d(4), d(5), d(6), d(7));
quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_512(defix, d(8), d(9), d(10), d(11), d(12), d(13), d(14),
d(15));
quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7);
/* Second row of quasigroup e-transformations */
LS1_512(defix, p[8], p[9], p[10], p[11], p[12], p[13], p[14],
p[15]);
LS2_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_512(defix, q0, q1, q2, q3, q4, q5, q6, q7);
quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7);
/* Third row of quasigroup e-transformations */
LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_512(defix, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_512(defix, q0, q1, q2, q3, q4, q5, q6, q7);
LS2_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7);
/* Fourth row of quasigroup e-transformations */
LS1_512(defix, d(7), d(6), d(5), d(4), d(3), d(2), d(1), d(0));
LS2_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
quasi_exform512(p0, p1, p2, p3, p4, p5, p6, p7);
LS1_512(defix, p0, p1, p2, p3, p4, p5, p6, p7);
LS2_512(defix, q0, q1, q2, q3, q4, q5, q6, q7);
quasi_exform512(q0, q1, q2, q3, q4, q5, q6, q7);
/* Edon-R tweak on the original SHA-3 Edon-R submission. */
p[0] ^= d(8) ^ p0;
p[1] ^= d(9) ^ p1;
p[2] ^= d(10) ^ p2;
p[3] ^= d(11) ^ p3;
p[4] ^= d(12) ^ p4;
p[5] ^= d(13) ^ p5;
p[6] ^= d(14) ^ p6;
p[7] ^= d(15) ^ p7;
p[8] ^= d(0) ^ q0;
p[9] ^= d(1) ^ q1;
p[10] ^= d(2) ^ q2;
p[11] ^= d(3) ^ q3;
p[12] ^= d(4) ^ q4;
p[13] ^= d(5) ^ q5;
p[14] ^= d(6) ^ q6;
p[15] ^= d(7) ^ q7;
}
#undef d
return (bitlen - bl);
}
void
EdonRInit(EdonRState *state, size_t hashbitlen)
{
ASSERT(EDONR_VALID_HASHBITLEN(hashbitlen));
switch (hashbitlen) {
case 224:
state->hashbitlen = 224;
state->bits_processed = 0;
state->unprocessed_bits = 0;
bcopy(i224p2, hashState224(state)->DoublePipe,
16 * sizeof (uint32_t));
break;
case 256:
state->hashbitlen = 256;
state->bits_processed = 0;
state->unprocessed_bits = 0;
bcopy(i256p2, hashState256(state)->DoublePipe,
16 * sizeof (uint32_t));
break;
case 384:
state->hashbitlen = 384;
state->bits_processed = 0;
state->unprocessed_bits = 0;
bcopy(i384p2, hashState384(state)->DoublePipe,
16 * sizeof (uint64_t));
break;
case 512:
state->hashbitlen = 512;
state->bits_processed = 0;
state->unprocessed_bits = 0;
bcopy(i512p2, hashState224(state)->DoublePipe,
16 * sizeof (uint64_t));
break;
}
}
void
EdonRUpdate(EdonRState *state, const uint8_t *data, size_t databitlen)
{
uint32_t *data32;
uint64_t *data64;
size_t bits_processed;
ASSERT(EDONR_VALID_HASHBITLEN(state->hashbitlen));
switch (state->hashbitlen) {
case 224:
case 256:
if (state->unprocessed_bits > 0) {
/* LastBytes = databitlen / 8 */
int LastBytes = (int)databitlen >> 3;
ASSERT(state->unprocessed_bits + databitlen <=
EdonR256_BLOCK_SIZE * 8);
bcopy(data, hashState256(state)->LastPart
+ (state->unprocessed_bits >> 3), LastBytes);
state->unprocessed_bits += (int)databitlen;
databitlen = state->unprocessed_bits;
/* LINTED E_BAD_PTR_CAST_ALIGN */
data32 = (uint32_t *)hashState256(state)->LastPart;
} else
/* LINTED E_BAD_PTR_CAST_ALIGN */
data32 = (uint32_t *)data;
bits_processed = Q256(databitlen, data32,
hashState256(state)->DoublePipe);
state->bits_processed += bits_processed;
databitlen -= bits_processed;
state->unprocessed_bits = (int)databitlen;
if (databitlen > 0) {
/* LastBytes = Ceil(databitlen / 8) */
int LastBytes =
((~(((-(int)databitlen) >> 3) & 0x01ff)) +
1) & 0x01ff;
data32 += bits_processed >> 5; /* byte size update */
bcopy(data32, hashState256(state)->LastPart, LastBytes);
}
break;
case 384:
case 512:
if (state->unprocessed_bits > 0) {
/* LastBytes = databitlen / 8 */
int LastBytes = (int)databitlen >> 3;
ASSERT(state->unprocessed_bits + databitlen <=
EdonR512_BLOCK_SIZE * 8);
bcopy(data, hashState512(state)->LastPart
+ (state->unprocessed_bits >> 3), LastBytes);
state->unprocessed_bits += (int)databitlen;
databitlen = state->unprocessed_bits;
/* LINTED E_BAD_PTR_CAST_ALIGN */
data64 = (uint64_t *)hashState512(state)->LastPart;
} else
/* LINTED E_BAD_PTR_CAST_ALIGN */
data64 = (uint64_t *)data;
bits_processed = Q512(databitlen, data64,
hashState512(state)->DoublePipe);
state->bits_processed += bits_processed;
databitlen -= bits_processed;
state->unprocessed_bits = (int)databitlen;
if (databitlen > 0) {
/* LastBytes = Ceil(databitlen / 8) */
int LastBytes =
((~(((-(int)databitlen) >> 3) & 0x03ff)) +
1) & 0x03ff;
data64 += bits_processed >> 6; /* byte size update */
bcopy(data64, hashState512(state)->LastPart, LastBytes);
}
break;
}
}
void
EdonRFinal(EdonRState *state, uint8_t *hashval)
{
uint32_t *data32;
uint64_t *data64, num_bits;
size_t databitlen;
int LastByte, PadOnePosition;
num_bits = state->bits_processed + state->unprocessed_bits;
ASSERT(EDONR_VALID_HASHBITLEN(state->hashbitlen));
switch (state->hashbitlen) {
case 224:
case 256:
LastByte = (int)state->unprocessed_bits >> 3;
PadOnePosition = 7 - (state->unprocessed_bits & 0x07);
hashState256(state)->LastPart[LastByte] =
(hashState256(state)->LastPart[LastByte]
& (0xff << (PadOnePosition + 1))) ^
(0x01 << PadOnePosition);
/* LINTED E_BAD_PTR_CAST_ALIGN */
data64 = (uint64_t *)hashState256(state)->LastPart;
if (state->unprocessed_bits < 448) {
(void) memset((hashState256(state)->LastPart) +
LastByte + 1, 0x00,
EdonR256_BLOCK_SIZE - LastByte - 9);
databitlen = EdonR256_BLOCK_SIZE * 8;
#if defined(MACHINE_IS_BIG_ENDIAN)
st_swap64(num_bits, data64 + 7);
#else
data64[7] = num_bits;
#endif
} else {
(void) memset((hashState256(state)->LastPart) +
LastByte + 1, 0x00,
EdonR256_BLOCK_SIZE * 2 - LastByte - 9);
databitlen = EdonR256_BLOCK_SIZE * 16;
#if defined(MACHINE_IS_BIG_ENDIAN)
st_swap64(num_bits, data64 + 15);
#else
data64[15] = num_bits;
#endif
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
data32 = (uint32_t *)hashState256(state)->LastPart;
state->bits_processed += Q256(databitlen, data32,
hashState256(state)->DoublePipe);
break;
case 384:
case 512:
LastByte = (int)state->unprocessed_bits >> 3;
PadOnePosition = 7 - (state->unprocessed_bits & 0x07);
hashState512(state)->LastPart[LastByte] =
(hashState512(state)->LastPart[LastByte]
& (0xff << (PadOnePosition + 1))) ^
(0x01 << PadOnePosition);
/* LINTED E_BAD_PTR_CAST_ALIGN */
data64 = (uint64_t *)hashState512(state)->LastPart;
if (state->unprocessed_bits < 960) {
(void) memset((hashState512(state)->LastPart) +
LastByte + 1, 0x00,
EdonR512_BLOCK_SIZE - LastByte - 9);
databitlen = EdonR512_BLOCK_SIZE * 8;
#if defined(MACHINE_IS_BIG_ENDIAN)
st_swap64(num_bits, data64 + 15);
#else
data64[15] = num_bits;
#endif
} else {
(void) memset((hashState512(state)->LastPart) +
LastByte + 1, 0x00,
EdonR512_BLOCK_SIZE * 2 - LastByte - 9);
databitlen = EdonR512_BLOCK_SIZE * 16;
#if defined(MACHINE_IS_BIG_ENDIAN)
st_swap64(num_bits, data64 + 31);
#else
data64[31] = num_bits;
#endif
}
state->bits_processed += Q512(databitlen, data64,
hashState512(state)->DoublePipe);
break;
}
switch (state->hashbitlen) {
case 224: {
#if defined(MACHINE_IS_BIG_ENDIAN)
uint32_t *d32 = (uint32_t *)hashval;
uint32_t *s32 = hashState224(state)->DoublePipe + 9;
int j;
for (j = 0; j < EdonR224_DIGEST_SIZE >> 2; j++)
st_swap32(s32[j], d32 + j);
#else
bcopy(hashState256(state)->DoublePipe + 9, hashval,
EdonR224_DIGEST_SIZE);
#endif
break;
}
case 256: {
#if defined(MACHINE_IS_BIG_ENDIAN)
uint32_t *d32 = (uint32_t *)hashval;
uint32_t *s32 = hashState224(state)->DoublePipe + 8;
int j;
for (j = 0; j < EdonR256_DIGEST_SIZE >> 2; j++)
st_swap32(s32[j], d32 + j);
#else
bcopy(hashState256(state)->DoublePipe + 8, hashval,
EdonR256_DIGEST_SIZE);
#endif
break;
}
case 384: {
#if defined(MACHINE_IS_BIG_ENDIAN)
uint64_t *d64 = (uint64_t *)hashval;
uint64_t *s64 = hashState384(state)->DoublePipe + 10;
int j;
for (j = 0; j < EdonR384_DIGEST_SIZE >> 3; j++)
st_swap64(s64[j], d64 + j);
#else
bcopy(hashState384(state)->DoublePipe + 10, hashval,
EdonR384_DIGEST_SIZE);
#endif
break;
}
case 512: {
#if defined(MACHINE_IS_BIG_ENDIAN)
uint64_t *d64 = (uint64_t *)hashval;
uint64_t *s64 = hashState512(state)->DoublePipe + 8;
int j;
for (j = 0; j < EdonR512_DIGEST_SIZE >> 3; j++)
st_swap64(s64[j], d64 + j);
#else
bcopy(hashState512(state)->DoublePipe + 8, hashval,
EdonR512_DIGEST_SIZE);
#endif
break;
}
}
}
void
EdonRHash(size_t hashbitlen, const uint8_t *data, size_t databitlen,
uint8_t *hashval)
{
EdonRState state;
EdonRInit(&state, hashbitlen);
EdonRUpdate(&state, data, databitlen);
EdonRFinal(&state, hashval);
}

View File

@ -0,0 +1,219 @@
/*
* IDI,NTNU
*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright (C) 2009, 2010, Jorn Amundsen <jorn.amundsen@ntnu.no>
*
* C header file to determine compile machine byte order. Take care when cross
* compiling.
*
* $Id: byteorder.h 517 2013-02-17 20:34:39Z joern $
*/
/*
* Portions copyright (c) 2013, Saso Kiselkov, All rights reserved
*/
#ifndef _CRYPTO_EDONR_BYTEORDER_H
#define _CRYPTO_EDONR_BYTEORDER_H
#if defined(__linux)
#include <endian.h>
#else
#include <sys/param.h>
#endif
#if defined(__BYTE_ORDER)
#if (__BYTE_ORDER == __BIG_ENDIAN)
#define MACHINE_IS_BIG_ENDIAN
#elif (__BYTE_ORDER == __LITTLE_ENDIAN)
#define MACHINE_IS_LITTLE_ENDIAN
#endif
#elif defined(BYTE_ORDER)
#if (BYTE_ORDER == BIG_ENDIAN)
#define MACHINE_IS_BIG_ENDIAN
#elif (BYTE_ORDER == LITTLE_ENDIAN)
#define MACHINE_IS_LITTLE_ENDIAN
#endif
#endif /* __BYTE_ORDER || BYTE_ORDER */
#if !defined(MACHINE_IS_BIG_ENDIAN) && !defined(MACHINE_IS_LITTLE_ENDIAN)
#if defined(_BIG_ENDIAN) || defined(_MIPSEB)
#define MACHINE_IS_BIG_ENDIAN
#endif
#if defined(_LITTLE_ENDIAN) || defined(_MIPSEL)
#define MACHINE_IS_LITTLE_ENDIAN
#endif
#endif /* !MACHINE_IS_BIG_ENDIAN && !MACHINE_IS_LITTLE_ENDIAN */
#if !defined(MACHINE_IS_BIG_ENDIAN) && !defined(MACHINE_IS_LITTLE_ENDIAN)
#error unknown machine byte sex
#endif
#define BYTEORDER_INCLUDED
#if defined(MACHINE_IS_BIG_ENDIAN)
/*
* Byte swapping macros for big endian architectures and compilers,
* add as appropriate for other architectures and/or compilers.
*
* ld_swap64(src,dst) : uint64_t dst = *(src)
* st_swap64(src,dst) : *(dst) = uint64_t src
*/
#if defined(__PPC__) || defined(_ARCH_PPC)
#if defined(__64BIT__)
#if defined(_ARCH_PWR7)
#define aix_ld_swap64(s64, d64)\
__asm__("ldbrx %0,0,%1" : "=r"(d64) : "r"(s64))
#define aix_st_swap64(s64, d64)\
__asm__ volatile("stdbrx %1,0,%0" : : "r"(d64), "r"(s64))
#else
#define aix_ld_swap64(s64, d64) \
{ \
uint64_t *s4, h; \
\
__asm__("addi %0,%3,4;lwbrx %1,0,%3;lwbrx %2,0,%0;rldimi %1,%2,32,0"\
: "+r"(s4), "=r"(d64), "=r"(h) : "b"(s64)); \
}
#define aix_st_swap64(s64, d64) \
{ \
uint64_t *s4, h; \
h = (s64) >> 32; \
__asm__ volatile("addi %0,%3,4;stwbrx %1,0,%3;stwbrx %2,0,%0" \
: "+r"(s4) : "r"(s64), "r"(h), "b"(d64)); \
}
#endif /* 64BIT && PWR7 */
#else
#define aix_ld_swap64(s64, d64) \
{ \
uint32_t *s4, h, l; \
__asm__("addi %0,%3,4;lwbrx %1,0,%3;lwbrx %2,0,%0" \
: "+r"(s4), "=r"(l), "=r"(h) : "b"(s64)); \
d64 = ((uint64_t)h<<32) | l; \
}
#define aix_st_swap64(s64, d64) \
{ \
uint32_t *s4, h, l; \
l = (s64) & 0xfffffffful, h = (s64) >> 32; \
__asm__ volatile("addi %0,%3,4;stwbrx %1,0,%3;stwbrx %2,0,%0" \
: "+r"(s4) : "r"(l), "r"(h), "b"(d64)); \
}
#endif /* __64BIT__ */
#define aix_ld_swap32(s32, d32)\
__asm__("lwbrx %0,0,%1" : "=r"(d32) : "r"(s32))
#define aix_st_swap32(s32, d32)\
__asm__ volatile("stwbrx %1,0,%0" : : "r"(d32), "r"(s32))
#define ld_swap32(s, d) aix_ld_swap32(s, d)
#define st_swap32(s, d) aix_st_swap32(s, d)
#define ld_swap64(s, d) aix_ld_swap64(s, d)
#define st_swap64(s, d) aix_st_swap64(s, d)
#endif /* __PPC__ || _ARCH_PPC */
#if defined(__sparc)
#if !defined(__arch64__) && !defined(__sparcv8) && defined(__sparcv9)
#define __arch64__
#endif
#if defined(__GNUC__) || (defined(__SUNPRO_C) && __SUNPRO_C > 0x590)
/* need Sun Studio C 5.10 and above for GNU inline assembly */
#if defined(__arch64__)
#define sparc_ld_swap64(s64, d64) \
__asm__("ldxa [%1]0x88,%0" : "=r"(d64) : "r"(s64))
#define sparc_st_swap64(s64, d64) \
__asm__ volatile("stxa %0,[%1]0x88" : : "r"(s64), "r"(d64))
#define st_swap64(s, d) sparc_st_swap64(s, d)
#else
#define sparc_ld_swap64(s64, d64) \
{ \
uint32_t *s4, h, l; \
__asm__("add %3,4,%0\n\tlda [%3]0x88,%1\n\tlda [%0]0x88,%2" \
: "+r"(s4), "=r"(l), "=r"(h) : "r"(s64)); \
d64 = ((uint64_t)h<<32) | l; \
}
#define sparc_st_swap64(s64, d64) \
{ \
uint32_t *s4, h, l; \
l = (s64) & 0xfffffffful, h = (s64) >> 32; \
__asm__ volatile("add %3,4,%0\n\tsta %1,[%3]0x88\n\tsta %2,[%0]0x88"\
: "+r"(s4) : "r"(l), "r"(h), "r"(d64)); \
}
#endif /* sparc64 */
#define sparc_ld_swap32(s32, d32)\
__asm__("lda [%1]0x88,%0" : "=r"(d32) : "r"(s32))
#define sparc_st_swap32(s32, d32)\
__asm__ volatile("sta %0,[%1]0x88" : : "r"(s32), "r"(d32))
#define ld_swap32(s, d) sparc_ld_swap32(s, d)
#define st_swap32(s, d) sparc_st_swap32(s, d)
#define ld_swap64(s, d) sparc_ld_swap64(s, d)
#define st_swap64(s, d) sparc_st_swap64(s, d)
#endif /* GCC || Sun Studio C > 5.9 */
#endif /* sparc */
/* GCC fallback */
#if ((__GNUC__ >= 4) || defined(__PGIC__)) && !defined(ld_swap32)
#define ld_swap32(s, d) (d = __builtin_bswap32(*(s)))
#define st_swap32(s, d) (*(d) = __builtin_bswap32(s))
#endif /* GCC4/PGIC && !swap32 */
#if ((__GNUC__ >= 4) || defined(__PGIC__)) && !defined(ld_swap64)
#define ld_swap64(s, d) (d = __builtin_bswap64(*(s)))
#define st_swap64(s, d) (*(d) = __builtin_bswap64(s))
#endif /* GCC4/PGIC && !swap64 */
/* generic fallback */
#if !defined(ld_swap32)
#define ld_swap32(s, d) \
(d = (*(s) >> 24) | (*(s) >> 8 & 0xff00) | \
(*(s) << 8 & 0xff0000) | (*(s) << 24))
#define st_swap32(s, d) \
(*(d) = ((s) >> 24) | ((s) >> 8 & 0xff00) | \
((s) << 8 & 0xff0000) | ((s) << 24))
#endif
#if !defined(ld_swap64)
#define ld_swap64(s, d) \
(d = (*(s) >> 56) | (*(s) >> 40 & 0xff00) | \
(*(s) >> 24 & 0xff0000) | (*(s) >> 8 & 0xff000000) | \
(*(s) & 0xff000000) << 8 | (*(s) & 0xff0000) << 24 | \
(*(s) & 0xff00) << 40 | *(s) << 56)
#define st_swap64(s, d) \
(*(d) = ((s) >> 56) | ((s) >> 40 & 0xff00) | \
((s) >> 24 & 0xff0000) | ((s) >> 8 & 0xff000000) | \
((s) & 0xff000000) << 8 | ((s) & 0xff0000) << 24 | \
((s) & 0xff00) << 40 | (s) << 56)
#endif
#endif /* MACHINE_IS_BIG_ENDIAN */
#if defined(MACHINE_IS_LITTLE_ENDIAN)
/* replace swaps with simple assignments on little endian systems */
#undef ld_swap32
#undef st_swap32
#define ld_swap32(s, d) (d = *(s))
#define st_swap32(s, d) (*(d) = s)
#undef ld_swap64
#undef st_swap64
#define ld_swap64(s, d) (d = *(s))
#define st_swap64(s, d) (*(d) = s)
#endif /* MACHINE_IS_LITTLE_ENDIAN */
#endif /* _CRYPTO_EDONR_BYTEORDER_H */

View File

@ -0,0 +1,3 @@
Implementation of the Skein hash function.
Source code author: Doug Whiting, 2008.
This algorithm and source code is released to the public domain.

View File

@ -0,0 +1 @@
LICENSE TERMS OF SKEIN HASH ALGORITHM IMPLEMENTATION

914
common/crypto/skein/skein.c Normal file
View File

@ -0,0 +1,914 @@
/*
* Implementation of the Skein hash function.
* Source code author: Doug Whiting, 2008.
* This algorithm and source code is released to the public domain.
*/
/* Copyright 2013 Doug Whiting. This code is released to the public domain. */
#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */
#include <sys/types.h>
#include <sys/note.h>
#include <sys/skein.h> /* get the Skein API definitions */
#include "skein_impl.h" /* get internal definitions */
/* External function to process blkCnt (nonzero) full block(s) of data. */
void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx, const uint8_t *blkPtr,
size_t blkCnt, size_t byteCntAdd);
void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx, const uint8_t *blkPtr,
size_t blkCnt, size_t byteCntAdd);
void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx, const uint8_t *blkPtr,
size_t blkCnt, size_t byteCntAdd);
/* 256-bit Skein */
/* init the context for a straight hashing operation */
int
Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen)
{
union {
uint8_t b[SKEIN_256_STATE_BYTES];
uint64_t w[SKEIN_256_STATE_WORDS];
} cfg; /* config block */
Skein_Assert(hashBitLen > 0, SKEIN_BAD_HASHLEN);
ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
switch (hashBitLen) { /* use pre-computed values, where available */
#ifndef SKEIN_NO_PRECOMP
case 256:
bcopy(SKEIN_256_IV_256, ctx->X, sizeof (ctx->X));
break;
case 224:
bcopy(SKEIN_256_IV_224, ctx->X, sizeof (ctx->X));
break;
case 160:
bcopy(SKEIN_256_IV_160, ctx->X, sizeof (ctx->X));
break;
case 128:
bcopy(SKEIN_256_IV_128, ctx->X, sizeof (ctx->X));
break;
#endif
default:
/* here if there is no precomputed IV value available */
/*
* build/process the config block, type == CONFIG (could be
* precomputed)
*/
/* set tweaks: T0=0; T1=CFG | FINAL */
Skein_Start_New_Type(ctx, CFG_FINAL);
/* set the schema, version */
cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
/* hash result length in bits */
cfg.w[1] = Skein_Swap64(hashBitLen);
cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
/* zero pad config block */
bzero(&cfg.w[3], sizeof (cfg) - 3 * sizeof (cfg.w[0]));
/* compute the initial chaining values from config block */
/* zero the chaining variables */
bzero(ctx->X, sizeof (ctx->X));
Skein_256_Process_Block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
break;
}
/*
* The chaining vars ctx->X are now initialized for the given
* hashBitLen.
* Set up to process the data message portion of the hash (default)
*/
Skein_Start_New_Type(ctx, MSG); /* T0=0, T1= MSG type */
return (SKEIN_SUCCESS);
}
/* init the context for a MAC and/or tree hash operation */
/*
* [identical to Skein_256_Init() when keyBytes == 0 &&
* treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL]
*/
int
Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen, uint64_t treeInfo,
const uint8_t *key, size_t keyBytes)
{
union {
uint8_t b[SKEIN_256_STATE_BYTES];
uint64_t w[SKEIN_256_STATE_WORDS];
} cfg; /* config block */
Skein_Assert(hashBitLen > 0, SKEIN_BAD_HASHLEN);
Skein_Assert(keyBytes == 0 || key != NULL, SKEIN_FAIL);
/* compute the initial chaining values ctx->X[], based on key */
if (keyBytes == 0) { /* is there a key? */
/* no key: use all zeroes as key for config block */
bzero(ctx->X, sizeof (ctx->X));
} else { /* here to pre-process a key */
Skein_assert(sizeof (cfg.b) >= sizeof (ctx->X));
/* do a mini-Init right here */
/* set output hash bit count = state size */
ctx->h.hashBitLen = 8 * sizeof (ctx->X);
/* set tweaks: T0 = 0; T1 = KEY type */
Skein_Start_New_Type(ctx, KEY);
/* zero the initial chaining variables */
bzero(ctx->X, sizeof (ctx->X));
/* hash the key */
(void) Skein_256_Update(ctx, key, keyBytes);
/* put result into cfg.b[] */
(void) Skein_256_Final_Pad(ctx, cfg.b);
/* copy over into ctx->X[] */
bcopy(cfg.b, ctx->X, sizeof (cfg.b));
#if SKEIN_NEED_SWAP
{
uint_t i;
/* convert key bytes to context words */
for (i = 0; i < SKEIN_256_STATE_WORDS; i++)
ctx->X[i] = Skein_Swap64(ctx->X[i]);
}
#endif
}
/*
* build/process the config block, type == CONFIG (could be
* precomputed for each key)
*/
ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
Skein_Start_New_Type(ctx, CFG_FINAL);
bzero(&cfg.w, sizeof (cfg.w)); /* pre-pad cfg.w[] with zeroes */
cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
/* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
cfg.w[2] = Skein_Swap64(treeInfo);
Skein_Show_Key(256, &ctx->h, key, keyBytes);
/* compute the initial chaining values from config block */
Skein_256_Process_Block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
/* The chaining vars ctx->X are now initialized */
/* Set up to process the data message portion of the hash (default) */
ctx->h.bCnt = 0; /* buffer b[] starts out empty */
Skein_Start_New_Type(ctx, MSG);
return (SKEIN_SUCCESS);
}
/* process the input bytes */
int
Skein_256_Update(Skein_256_Ctxt_t *ctx, const uint8_t *msg, size_t msgByteCnt)
{
size_t n;
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
/* process full blocks, if any */
if (msgByteCnt + ctx->h.bCnt > SKEIN_256_BLOCK_BYTES) {
/* finish up any buffered message data */
if (ctx->h.bCnt) {
/* # bytes free in buffer b[] */
n = SKEIN_256_BLOCK_BYTES - ctx->h.bCnt;
if (n) {
/* check on our logic here */
Skein_assert(n < msgByteCnt);
bcopy(msg, &ctx->b[ctx->h.bCnt], n);
msgByteCnt -= n;
msg += n;
ctx->h.bCnt += n;
}
Skein_assert(ctx->h.bCnt == SKEIN_256_BLOCK_BYTES);
Skein_256_Process_Block(ctx, ctx->b, 1,
SKEIN_256_BLOCK_BYTES);
ctx->h.bCnt = 0;
}
/*
* now process any remaining full blocks, directly from input
* message data
*/
if (msgByteCnt > SKEIN_256_BLOCK_BYTES) {
/* number of full blocks to process */
n = (msgByteCnt - 1) / SKEIN_256_BLOCK_BYTES;
Skein_256_Process_Block(ctx, msg, n,
SKEIN_256_BLOCK_BYTES);
msgByteCnt -= n * SKEIN_256_BLOCK_BYTES;
msg += n * SKEIN_256_BLOCK_BYTES;
}
Skein_assert(ctx->h.bCnt == 0);
}
/* copy any remaining source message data bytes into b[] */
if (msgByteCnt) {
Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES);
bcopy(msg, &ctx->b[ctx->h.bCnt], msgByteCnt);
ctx->h.bCnt += msgByteCnt;
}
return (SKEIN_SUCCESS);
}
/* finalize the hash computation and output the result */
int
Skein_256_Final(Skein_256_Ctxt_t *ctx, uint8_t *hashVal)
{
size_t i, n, byteCnt;
uint64_t X[SKEIN_256_STATE_WORDS];
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
/* zero pad b[] if necessary */
if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES)
bzero(&ctx->b[ctx->h.bCnt],
SKEIN_256_BLOCK_BYTES - ctx->h.bCnt);
/* process the final block */
Skein_256_Process_Block(ctx, ctx->b, 1, ctx->h.bCnt);
/* now output the result */
/* total number of output bytes */
byteCnt = (ctx->h.hashBitLen + 7) >> 3;
/* run Threefish in "counter mode" to generate output */
/* zero out b[], so it can hold the counter */
bzero(ctx->b, sizeof (ctx->b));
/* keep a local copy of counter mode "key" */
bcopy(ctx->X, X, sizeof (X));
for (i = 0; i * SKEIN_256_BLOCK_BYTES < byteCnt; i++) {
/* build the counter block */
uint64_t tmp = Skein_Swap64((uint64_t)i);
bcopy(&tmp, ctx->b, sizeof (tmp));
Skein_Start_New_Type(ctx, OUT_FINAL);
/* run "counter mode" */
Skein_256_Process_Block(ctx, ctx->b, 1, sizeof (uint64_t));
/* number of output bytes left to go */
n = byteCnt - i * SKEIN_256_BLOCK_BYTES;
if (n >= SKEIN_256_BLOCK_BYTES)
n = SKEIN_256_BLOCK_BYTES;
Skein_Put64_LSB_First(hashVal + i * SKEIN_256_BLOCK_BYTES,
ctx->X, n); /* "output" the ctr mode bytes */
Skein_Show_Final(256, &ctx->h, n,
hashVal + i * SKEIN_256_BLOCK_BYTES);
/* restore the counter mode key for next time */
bcopy(X, ctx->X, sizeof (X));
}
return (SKEIN_SUCCESS);
}
/* 512-bit Skein */
/* init the context for a straight hashing operation */
int
Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen)
{
union {
uint8_t b[SKEIN_512_STATE_BYTES];
uint64_t w[SKEIN_512_STATE_WORDS];
} cfg; /* config block */
Skein_Assert(hashBitLen > 0, SKEIN_BAD_HASHLEN);
ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
switch (hashBitLen) { /* use pre-computed values, where available */
#ifndef SKEIN_NO_PRECOMP
case 512:
bcopy(SKEIN_512_IV_512, ctx->X, sizeof (ctx->X));
break;
case 384:
bcopy(SKEIN_512_IV_384, ctx->X, sizeof (ctx->X));
break;
case 256:
bcopy(SKEIN_512_IV_256, ctx->X, sizeof (ctx->X));
break;
case 224:
bcopy(SKEIN_512_IV_224, ctx->X, sizeof (ctx->X));
break;
#endif
default:
/*
* here if there is no precomputed IV value available
* build/process the config block, type == CONFIG (could be
* precomputed)
*/
/* set tweaks: T0=0; T1=CFG | FINAL */
Skein_Start_New_Type(ctx, CFG_FINAL);
/* set the schema, version */
cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
/* hash result length in bits */
cfg.w[1] = Skein_Swap64(hashBitLen);
cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
/* zero pad config block */
bzero(&cfg.w[3], sizeof (cfg) - 3 * sizeof (cfg.w[0]));
/* compute the initial chaining values from config block */
/* zero the chaining variables */
bzero(ctx->X, sizeof (ctx->X));
Skein_512_Process_Block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
break;
}
/*
* The chaining vars ctx->X are now initialized for the given
* hashBitLen. Set up to process the data message portion of the
* hash (default)
*/
Skein_Start_New_Type(ctx, MSG); /* T0=0, T1= MSG type */
return (SKEIN_SUCCESS);
}
/* init the context for a MAC and/or tree hash operation */
/*
* [identical to Skein_512_Init() when keyBytes == 0 &&
* treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL]
*/
int
Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen, uint64_t treeInfo,
const uint8_t *key, size_t keyBytes)
{
union {
uint8_t b[SKEIN_512_STATE_BYTES];
uint64_t w[SKEIN_512_STATE_WORDS];
} cfg; /* config block */
Skein_Assert(hashBitLen > 0, SKEIN_BAD_HASHLEN);
Skein_Assert(keyBytes == 0 || key != NULL, SKEIN_FAIL);
/* compute the initial chaining values ctx->X[], based on key */
if (keyBytes == 0) { /* is there a key? */
/* no key: use all zeroes as key for config block */
bzero(ctx->X, sizeof (ctx->X));
} else { /* here to pre-process a key */
Skein_assert(sizeof (cfg.b) >= sizeof (ctx->X));
/* do a mini-Init right here */
/* set output hash bit count = state size */
ctx->h.hashBitLen = 8 * sizeof (ctx->X);
/* set tweaks: T0 = 0; T1 = KEY type */
Skein_Start_New_Type(ctx, KEY);
/* zero the initial chaining variables */
bzero(ctx->X, sizeof (ctx->X));
(void) Skein_512_Update(ctx, key, keyBytes); /* hash the key */
/* put result into cfg.b[] */
(void) Skein_512_Final_Pad(ctx, cfg.b);
/* copy over into ctx->X[] */
bcopy(cfg.b, ctx->X, sizeof (cfg.b));
#if SKEIN_NEED_SWAP
{
uint_t i;
/* convert key bytes to context words */
for (i = 0; i < SKEIN_512_STATE_WORDS; i++)
ctx->X[i] = Skein_Swap64(ctx->X[i]);
}
#endif
}
/*
* build/process the config block, type == CONFIG (could be
* precomputed for each key)
*/
ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
Skein_Start_New_Type(ctx, CFG_FINAL);
bzero(&cfg.w, sizeof (cfg.w)); /* pre-pad cfg.w[] with zeroes */
cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */
/* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
cfg.w[2] = Skein_Swap64(treeInfo);
Skein_Show_Key(512, &ctx->h, key, keyBytes);
/* compute the initial chaining values from config block */
Skein_512_Process_Block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
/* The chaining vars ctx->X are now initialized */
/* Set up to process the data message portion of the hash (default) */
ctx->h.bCnt = 0; /* buffer b[] starts out empty */
Skein_Start_New_Type(ctx, MSG);
return (SKEIN_SUCCESS);
}
/* process the input bytes */
int
Skein_512_Update(Skein_512_Ctxt_t *ctx, const uint8_t *msg, size_t msgByteCnt)
{
size_t n;
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
/* process full blocks, if any */
if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES) {
/* finish up any buffered message data */
if (ctx->h.bCnt) {
/* # bytes free in buffer b[] */
n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt;
if (n) {
/* check on our logic here */
Skein_assert(n < msgByteCnt);
bcopy(msg, &ctx->b[ctx->h.bCnt], n);
msgByteCnt -= n;
msg += n;
ctx->h.bCnt += n;
}
Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES);
Skein_512_Process_Block(ctx, ctx->b, 1,
SKEIN_512_BLOCK_BYTES);
ctx->h.bCnt = 0;
}
/*
* now process any remaining full blocks, directly from input
* message data
*/
if (msgByteCnt > SKEIN_512_BLOCK_BYTES) {
/* number of full blocks to process */
n = (msgByteCnt - 1) / SKEIN_512_BLOCK_BYTES;
Skein_512_Process_Block(ctx, msg, n,
SKEIN_512_BLOCK_BYTES);
msgByteCnt -= n * SKEIN_512_BLOCK_BYTES;
msg += n * SKEIN_512_BLOCK_BYTES;
}
Skein_assert(ctx->h.bCnt == 0);
}
/* copy any remaining source message data bytes into b[] */
if (msgByteCnt) {
Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES);
bcopy(msg, &ctx->b[ctx->h.bCnt], msgByteCnt);
ctx->h.bCnt += msgByteCnt;
}
return (SKEIN_SUCCESS);
}
/* finalize the hash computation and output the result */
int
Skein_512_Final(Skein_512_Ctxt_t *ctx, uint8_t *hashVal)
{
size_t i, n, byteCnt;
uint64_t X[SKEIN_512_STATE_WORDS];
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
/* zero pad b[] if necessary */
if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES)
bzero(&ctx->b[ctx->h.bCnt],
SKEIN_512_BLOCK_BYTES - ctx->h.bCnt);
/* process the final block */
Skein_512_Process_Block(ctx, ctx->b, 1, ctx->h.bCnt);
/* now output the result */
/* total number of output bytes */
byteCnt = (ctx->h.hashBitLen + 7) >> 3;
/* run Threefish in "counter mode" to generate output */
/* zero out b[], so it can hold the counter */
bzero(ctx->b, sizeof (ctx->b));
/* keep a local copy of counter mode "key" */
bcopy(ctx->X, X, sizeof (X));
for (i = 0; i * SKEIN_512_BLOCK_BYTES < byteCnt; i++) {
/* build the counter block */
uint64_t tmp = Skein_Swap64((uint64_t)i);
bcopy(&tmp, ctx->b, sizeof (tmp));
Skein_Start_New_Type(ctx, OUT_FINAL);
/* run "counter mode" */
Skein_512_Process_Block(ctx, ctx->b, 1, sizeof (uint64_t));
/* number of output bytes left to go */
n = byteCnt - i * SKEIN_512_BLOCK_BYTES;
if (n >= SKEIN_512_BLOCK_BYTES)
n = SKEIN_512_BLOCK_BYTES;
Skein_Put64_LSB_First(hashVal + i * SKEIN_512_BLOCK_BYTES,
ctx->X, n); /* "output" the ctr mode bytes */
Skein_Show_Final(512, &ctx->h, n,
hashVal + i * SKEIN_512_BLOCK_BYTES);
/* restore the counter mode key for next time */
bcopy(X, ctx->X, sizeof (X));
}
return (SKEIN_SUCCESS);
}
/* 1024-bit Skein */
/* init the context for a straight hashing operation */
int
Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen)
{
union {
uint8_t b[SKEIN1024_STATE_BYTES];
uint64_t w[SKEIN1024_STATE_WORDS];
} cfg; /* config block */
Skein_Assert(hashBitLen > 0, SKEIN_BAD_HASHLEN);
ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
switch (hashBitLen) { /* use pre-computed values, where available */
#ifndef SKEIN_NO_PRECOMP
case 512:
bcopy(SKEIN1024_IV_512, ctx->X, sizeof (ctx->X));
break;
case 384:
bcopy(SKEIN1024_IV_384, ctx->X, sizeof (ctx->X));
break;
case 1024:
bcopy(SKEIN1024_IV_1024, ctx->X, sizeof (ctx->X));
break;
#endif
default:
/* here if there is no precomputed IV value available */
/*
* build/process the config block, type == CONFIG (could be
* precomputed)
*/
/* set tweaks: T0=0; T1=CFG | FINAL */
Skein_Start_New_Type(ctx, CFG_FINAL);
/* set the schema, version */
cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
/* hash result length in bits */
cfg.w[1] = Skein_Swap64(hashBitLen);
cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
/* zero pad config block */
bzero(&cfg.w[3], sizeof (cfg) - 3 * sizeof (cfg.w[0]));
/* compute the initial chaining values from config block */
/* zero the chaining variables */
bzero(ctx->X, sizeof (ctx->X));
Skein1024_Process_Block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
break;
}
/*
* The chaining vars ctx->X are now initialized for the given
* hashBitLen. Set up to process the data message portion of the hash
* (default)
*/
Skein_Start_New_Type(ctx, MSG); /* T0=0, T1= MSG type */
return (SKEIN_SUCCESS);
}
/* init the context for a MAC and/or tree hash operation */
/*
* [identical to Skein1024_Init() when keyBytes == 0 &&
* treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL]
*/
int
Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen, uint64_t treeInfo,
const uint8_t *key, size_t keyBytes)
{
union {
uint8_t b[SKEIN1024_STATE_BYTES];
uint64_t w[SKEIN1024_STATE_WORDS];
} cfg; /* config block */
Skein_Assert(hashBitLen > 0, SKEIN_BAD_HASHLEN);
Skein_Assert(keyBytes == 0 || key != NULL, SKEIN_FAIL);
/* compute the initial chaining values ctx->X[], based on key */
if (keyBytes == 0) { /* is there a key? */
/* no key: use all zeroes as key for config block */
bzero(ctx->X, sizeof (ctx->X));
} else { /* here to pre-process a key */
Skein_assert(sizeof (cfg.b) >= sizeof (ctx->X));
/* do a mini-Init right here */
/* set output hash bit count = state size */
ctx->h.hashBitLen = 8 * sizeof (ctx->X);
/* set tweaks: T0 = 0; T1 = KEY type */
Skein_Start_New_Type(ctx, KEY);
/* zero the initial chaining variables */
bzero(ctx->X, sizeof (ctx->X));
(void) Skein1024_Update(ctx, key, keyBytes); /* hash the key */
/* put result into cfg.b[] */
(void) Skein1024_Final_Pad(ctx, cfg.b);
/* copy over into ctx->X[] */
bcopy(cfg.b, ctx->X, sizeof (cfg.b));
#if SKEIN_NEED_SWAP
{
uint_t i;
/* convert key bytes to context words */
for (i = 0; i < SKEIN1024_STATE_WORDS; i++)
ctx->X[i] = Skein_Swap64(ctx->X[i]);
}
#endif
}
/*
* build/process the config block, type == CONFIG (could be
* precomputed for each key)
*/
ctx->h.hashBitLen = hashBitLen; /* output hash bit count */
Skein_Start_New_Type(ctx, CFG_FINAL);
bzero(&cfg.w, sizeof (cfg.w)); /* pre-pad cfg.w[] with zeroes */
cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER);
/* hash result length in bits */
cfg.w[1] = Skein_Swap64(hashBitLen);
/* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
cfg.w[2] = Skein_Swap64(treeInfo);
Skein_Show_Key(1024, &ctx->h, key, keyBytes);
/* compute the initial chaining values from config block */
Skein1024_Process_Block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
/* The chaining vars ctx->X are now initialized */
/* Set up to process the data message portion of the hash (default) */
ctx->h.bCnt = 0; /* buffer b[] starts out empty */
Skein_Start_New_Type(ctx, MSG);
return (SKEIN_SUCCESS);
}
/* process the input bytes */
int
Skein1024_Update(Skein1024_Ctxt_t *ctx, const uint8_t *msg, size_t msgByteCnt)
{
size_t n;
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES, SKEIN_FAIL);
/* process full blocks, if any */
if (msgByteCnt + ctx->h.bCnt > SKEIN1024_BLOCK_BYTES) {
/* finish up any buffered message data */
if (ctx->h.bCnt) {
/* # bytes free in buffer b[] */
n = SKEIN1024_BLOCK_BYTES - ctx->h.bCnt;
if (n) {
/* check on our logic here */
Skein_assert(n < msgByteCnt);
bcopy(msg, &ctx->b[ctx->h.bCnt], n);
msgByteCnt -= n;
msg += n;
ctx->h.bCnt += n;
}
Skein_assert(ctx->h.bCnt == SKEIN1024_BLOCK_BYTES);
Skein1024_Process_Block(ctx, ctx->b, 1,
SKEIN1024_BLOCK_BYTES);
ctx->h.bCnt = 0;
}
/*
* now process any remaining full blocks, directly from
* input message data
*/
if (msgByteCnt > SKEIN1024_BLOCK_BYTES) {
/* number of full blocks to process */
n = (msgByteCnt - 1) / SKEIN1024_BLOCK_BYTES;
Skein1024_Process_Block(ctx, msg, n,
SKEIN1024_BLOCK_BYTES);
msgByteCnt -= n * SKEIN1024_BLOCK_BYTES;
msg += n * SKEIN1024_BLOCK_BYTES;
}
Skein_assert(ctx->h.bCnt == 0);
}
/* copy any remaining source message data bytes into b[] */
if (msgByteCnt) {
Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES);
bcopy(msg, &ctx->b[ctx->h.bCnt], msgByteCnt);
ctx->h.bCnt += msgByteCnt;
}
return (SKEIN_SUCCESS);
}
/* finalize the hash computation and output the result */
int
Skein1024_Final(Skein1024_Ctxt_t *ctx, uint8_t *hashVal)
{
size_t i, n, byteCnt;
uint64_t X[SKEIN1024_STATE_WORDS];
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES, SKEIN_FAIL);
ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
/* zero pad b[] if necessary */
if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES)
bzero(&ctx->b[ctx->h.bCnt],
SKEIN1024_BLOCK_BYTES - ctx->h.bCnt);
/* process the final block */
Skein1024_Process_Block(ctx, ctx->b, 1, ctx->h.bCnt);
/* now output the result */
/* total number of output bytes */
byteCnt = (ctx->h.hashBitLen + 7) >> 3;
/* run Threefish in "counter mode" to generate output */
/* zero out b[], so it can hold the counter */
bzero(ctx->b, sizeof (ctx->b));
/* keep a local copy of counter mode "key" */
bcopy(ctx->X, X, sizeof (X));
for (i = 0; i * SKEIN1024_BLOCK_BYTES < byteCnt; i++) {
/* build the counter block */
uint64_t tmp = Skein_Swap64((uint64_t)i);
bcopy(&tmp, ctx->b, sizeof (tmp));
Skein_Start_New_Type(ctx, OUT_FINAL);
/* run "counter mode" */
Skein1024_Process_Block(ctx, ctx->b, 1, sizeof (uint64_t));
/* number of output bytes left to go */
n = byteCnt - i * SKEIN1024_BLOCK_BYTES;
if (n >= SKEIN1024_BLOCK_BYTES)
n = SKEIN1024_BLOCK_BYTES;
Skein_Put64_LSB_First(hashVal + i * SKEIN1024_BLOCK_BYTES,
ctx->X, n); /* "output" the ctr mode bytes */
Skein_Show_Final(1024, &ctx->h, n,
hashVal + i * SKEIN1024_BLOCK_BYTES);
/* restore the counter mode key for next time */
bcopy(X, ctx->X, sizeof (X));
}
return (SKEIN_SUCCESS);
}
/* Functions to support MAC/tree hashing */
/* (this code is identical for Optimized and Reference versions) */
/* finalize the hash computation and output the block, no OUTPUT stage */
int
Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, uint8_t *hashVal)
{
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
/* zero pad b[] if necessary */
if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES)
bzero(&ctx->b[ctx->h.bCnt],
SKEIN_256_BLOCK_BYTES - ctx->h.bCnt);
/* process the final block */
Skein_256_Process_Block(ctx, ctx->b, 1, ctx->h.bCnt);
/* "output" the state bytes */
Skein_Put64_LSB_First(hashVal, ctx->X, SKEIN_256_BLOCK_BYTES);
return (SKEIN_SUCCESS);
}
/* finalize the hash computation and output the block, no OUTPUT stage */
int
Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, uint8_t *hashVal)
{
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */
/* zero pad b[] if necessary */
if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES)
bzero(&ctx->b[ctx->h.bCnt],
SKEIN_512_BLOCK_BYTES - ctx->h.bCnt);
/* process the final block */
Skein_512_Process_Block(ctx, ctx->b, 1, ctx->h.bCnt);
/* "output" the state bytes */
Skein_Put64_LSB_First(hashVal, ctx->X, SKEIN_512_BLOCK_BYTES);
return (SKEIN_SUCCESS);
}
/* finalize the hash computation and output the block, no OUTPUT stage */
int
Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, uint8_t *hashVal)
{
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES, SKEIN_FAIL);
/* tag as the final block */
ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL;
/* zero pad b[] if necessary */
if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES)
bzero(&ctx->b[ctx->h.bCnt],
SKEIN1024_BLOCK_BYTES - ctx->h.bCnt);
/* process the final block */
Skein1024_Process_Block(ctx, ctx->b, 1, ctx->h.bCnt);
/* "output" the state bytes */
Skein_Put64_LSB_First(hashVal, ctx->X, SKEIN1024_BLOCK_BYTES);
return (SKEIN_SUCCESS);
}
#if SKEIN_TREE_HASH
/* just do the OUTPUT stage */
int
Skein_256_Output(Skein_256_Ctxt_t *ctx, uint8_t *hashVal)
{
size_t i, n, byteCnt;
uint64_t X[SKEIN_256_STATE_WORDS];
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
/* now output the result */
/* total number of output bytes */
byteCnt = (ctx->h.hashBitLen + 7) >> 3;
/* run Threefish in "counter mode" to generate output */
/* zero out b[], so it can hold the counter */
bzero(ctx->b, sizeof (ctx->b));
/* keep a local copy of counter mode "key" */
bcopy(ctx->X, X, sizeof (X));
for (i = 0; i * SKEIN_256_BLOCK_BYTES < byteCnt; i++) {
/* build the counter block */
uint64_t tmp = Skein_Swap64((uint64_t)i);
bcopy(&tmp, ctx->b, sizeof (tmp));
Skein_Start_New_Type(ctx, OUT_FINAL);
/* run "counter mode" */
Skein_256_Process_Block(ctx, ctx->b, 1, sizeof (uint64_t));
/* number of output bytes left to go */
n = byteCnt - i * SKEIN_256_BLOCK_BYTES;
if (n >= SKEIN_256_BLOCK_BYTES)
n = SKEIN_256_BLOCK_BYTES;
Skein_Put64_LSB_First(hashVal + i * SKEIN_256_BLOCK_BYTES,
ctx->X, n); /* "output" the ctr mode bytes */
Skein_Show_Final(256, &ctx->h, n,
hashVal + i * SKEIN_256_BLOCK_BYTES);
/* restore the counter mode key for next time */
bcopy(X, ctx->X, sizeof (X));
}
return (SKEIN_SUCCESS);
}
/* just do the OUTPUT stage */
int
Skein_512_Output(Skein_512_Ctxt_t *ctx, uint8_t *hashVal)
{
size_t i, n, byteCnt;
uint64_t X[SKEIN_512_STATE_WORDS];
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
/* now output the result */
/* total number of output bytes */
byteCnt = (ctx->h.hashBitLen + 7) >> 3;
/* run Threefish in "counter mode" to generate output */
/* zero out b[], so it can hold the counter */
bzero(ctx->b, sizeof (ctx->b));
/* keep a local copy of counter mode "key" */
bcopy(ctx->X, X, sizeof (X));
for (i = 0; i * SKEIN_512_BLOCK_BYTES < byteCnt; i++) {
/* build the counter block */
uint64_t tmp = Skein_Swap64((uint64_t)i);
bcopy(&tmp, ctx->b, sizeof (tmp));
Skein_Start_New_Type(ctx, OUT_FINAL);
/* run "counter mode" */
Skein_512_Process_Block(ctx, ctx->b, 1, sizeof (uint64_t));
/* number of output bytes left to go */
n = byteCnt - i * SKEIN_512_BLOCK_BYTES;
if (n >= SKEIN_512_BLOCK_BYTES)
n = SKEIN_512_BLOCK_BYTES;
Skein_Put64_LSB_First(hashVal + i * SKEIN_512_BLOCK_BYTES,
ctx->X, n); /* "output" the ctr mode bytes */
Skein_Show_Final(256, &ctx->h, n,
hashVal + i * SKEIN_512_BLOCK_BYTES);
/* restore the counter mode key for next time */
bcopy(X, ctx->X, sizeof (X));
}
return (SKEIN_SUCCESS);
}
/* just do the OUTPUT stage */
int
Skein1024_Output(Skein1024_Ctxt_t *ctx, uint8_t *hashVal)
{
size_t i, n, byteCnt;
uint64_t X[SKEIN1024_STATE_WORDS];
/* catch uninitialized context */
Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES, SKEIN_FAIL);
/* now output the result */
/* total number of output bytes */
byteCnt = (ctx->h.hashBitLen + 7) >> 3;
/* run Threefish in "counter mode" to generate output */
/* zero out b[], so it can hold the counter */
bzero(ctx->b, sizeof (ctx->b));
/* keep a local copy of counter mode "key" */
bcopy(ctx->X, X, sizeof (X));
for (i = 0; i * SKEIN1024_BLOCK_BYTES < byteCnt; i++) {
/* build the counter block */
uint64_t tmp = Skein_Swap64((uint64_t)i);
bcopy(&tmp, ctx->b, sizeof (tmp));
Skein_Start_New_Type(ctx, OUT_FINAL);
/* run "counter mode" */
Skein1024_Process_Block(ctx, ctx->b, 1, sizeof (uint64_t));
/* number of output bytes left to go */
n = byteCnt - i * SKEIN1024_BLOCK_BYTES;
if (n >= SKEIN1024_BLOCK_BYTES)
n = SKEIN1024_BLOCK_BYTES;
Skein_Put64_LSB_First(hashVal + i * SKEIN1024_BLOCK_BYTES,
ctx->X, n); /* "output" the ctr mode bytes */
Skein_Show_Final(256, &ctx->h, n,
hashVal + i * SKEIN1024_BLOCK_BYTES);
/* restore the counter mode key for next time */
bcopy(X, ctx->X, sizeof (X));
}
return (SKEIN_SUCCESS);
}
#endif

View File

@ -0,0 +1,767 @@
/*
* Implementation of the Skein block functions.
* Source code author: Doug Whiting, 2008.
* This algorithm and source code is released to the public domain.
* Compile-time switches:
* SKEIN_USE_ASM -- set bits (256/512/1024) to select which
* versions use ASM code for block processing
* [default: use C for all block sizes]
*/
/* Copyright 2013 Doug Whiting. This code is released to the public domain. */
#include <sys/skein.h>
#include "skein_impl.h"
#ifndef SKEIN_USE_ASM
#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */
#endif
#ifndef SKEIN_LOOP
#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */
#endif
/* some useful definitions for code here */
#define BLK_BITS (WCNT*64)
#define KW_TWK_BASE (0)
#define KW_KEY_BASE (3)
#define ks (kw + KW_KEY_BASE)
#define ts (kw + KW_TWK_BASE)
/* no debugging in Illumos version */
#define DebugSaveTweak(ctx)
/* Skein_256 */
#if !(SKEIN_USE_ASM & 256)
void
Skein_256_Process_Block(Skein_256_Ctxt_t *ctx, const uint8_t *blkPtr,
size_t blkCnt, size_t byteCntAdd)
{ /* do it in C */
enum {
WCNT = SKEIN_256_STATE_WORDS
};
#undef RCNT
#define RCNT (SKEIN_256_ROUNDS_TOTAL / 8)
#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
#define SKEIN_UNROLL_256 (((SKEIN_LOOP) / 100) % 10)
#else
#define SKEIN_UNROLL_256 (0)
#endif
#if SKEIN_UNROLL_256
#if (RCNT % SKEIN_UNROLL_256)
#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */
#endif
size_t r;
/* key schedule words : chaining vars + tweak + "rotation" */
uint64_t kw[WCNT + 4 + RCNT * 2];
#else
uint64_t kw[WCNT + 4]; /* key schedule words : chaining vars + tweak */
#endif
/* local copy of context vars, for speed */
uint64_t X0, X1, X2, X3;
uint64_t w[WCNT]; /* local copy of input block */
#ifdef SKEIN_DEBUG
/* use for debugging (help compiler put Xn in registers) */
const uint64_t *Xptr[4];
Xptr[0] = &X0;
Xptr[1] = &X1;
Xptr[2] = &X2;
Xptr[3] = &X3;
#endif
Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */
ts[0] = ctx->h.T[0];
ts[1] = ctx->h.T[1];
do {
/*
* this implementation only supports 2**64 input bytes
* (no carry out here)
*/
ts[0] += byteCntAdd; /* update processed length */
/* precompute the key schedule for this block */
ks[0] = ctx->X[0];
ks[1] = ctx->X[1];
ks[2] = ctx->X[2];
ks[3] = ctx->X[3];
ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY;
ts[2] = ts[0] ^ ts[1];
/* get input block in little-endian format */
Skein_Get64_LSB_First(w, blkPtr, WCNT);
DebugSaveTweak(ctx);
Skein_Show_Block(BLK_BITS, &ctx->h, ctx->X, blkPtr, w, ks, ts);
X0 = w[0] + ks[0]; /* do the first full key injection */
X1 = w[1] + ks[1] + ts[0];
X2 = w[2] + ks[2] + ts[1];
X3 = w[3] + ks[3];
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INITIAL,
Xptr); /* show starting state values */
blkPtr += SKEIN_256_BLOCK_BYTES;
/* run the rounds */
#define Round256(p0, p1, p2, p3, ROT, rNum) \
X##p0 += X##p1; X##p1 = RotL_64(X##p1, ROT##_0); X##p1 ^= X##p0; \
X##p2 += X##p3; X##p3 = RotL_64(X##p3, ROT##_1); X##p3 ^= X##p2; \
#if SKEIN_UNROLL_256 == 0
#define R256(p0, p1, p2, p3, ROT, rNum) /* fully unrolled */ \
Round256(p0, p1, p2, p3, ROT, rNum) \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, rNum, Xptr);
#define I256(R) \
X0 += ks[((R) + 1) % 5]; /* inject the key schedule value */ \
X1 += ks[((R) + 2) % 5] + ts[((R) + 1) % 3]; \
X2 += ks[((R) + 3) % 5] + ts[((R) + 2) % 3]; \
X3 += ks[((R) + 4) % 5] + (R) + 1; \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INJECT, Xptr);
#else /* looping version */
#define R256(p0, p1, p2, p3, ROT, rNum) \
Round256(p0, p1, p2, p3, ROT, rNum) \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, 4 * (r - 1) + rNum, Xptr);
#define I256(R) \
X0 += ks[r + (R) + 0]; /* inject the key schedule value */ \
X1 += ks[r + (R) + 1] + ts[r + (R) + 0]; \
X2 += ks[r + (R) + 2] + ts[r + (R) + 1]; \
X3 += ks[r + (R) + 3] + r + (R); \
ks[r + (R) + 4] = ks[r + (R) - 1]; /* rotate key schedule */ \
ts[r + (R) + 2] = ts[r + (R) - 1]; \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INJECT, Xptr);
/* loop thru it */
for (r = 1; r < 2 * RCNT; r += 2 * SKEIN_UNROLL_256)
#endif
{
#define R256_8_rounds(R) \
R256(0, 1, 2, 3, R_256_0, 8 * (R) + 1); \
R256(0, 3, 2, 1, R_256_1, 8 * (R) + 2); \
R256(0, 1, 2, 3, R_256_2, 8 * (R) + 3); \
R256(0, 3, 2, 1, R_256_3, 8 * (R) + 4); \
I256(2 * (R)); \
R256(0, 1, 2, 3, R_256_4, 8 * (R) + 5); \
R256(0, 3, 2, 1, R_256_5, 8 * (R) + 6); \
R256(0, 1, 2, 3, R_256_6, 8 * (R) + 7); \
R256(0, 3, 2, 1, R_256_7, 8 * (R) + 8); \
I256(2 * (R) + 1);
R256_8_rounds(0);
#define R256_Unroll_R(NN) \
((SKEIN_UNROLL_256 == 0 && SKEIN_256_ROUNDS_TOTAL / 8 > (NN)) || \
(SKEIN_UNROLL_256 > (NN)))
#if R256_Unroll_R(1)
R256_8_rounds(1);
#endif
#if R256_Unroll_R(2)
R256_8_rounds(2);
#endif
#if R256_Unroll_R(3)
R256_8_rounds(3);
#endif
#if R256_Unroll_R(4)
R256_8_rounds(4);
#endif
#if R256_Unroll_R(5)
R256_8_rounds(5);
#endif
#if R256_Unroll_R(6)
R256_8_rounds(6);
#endif
#if R256_Unroll_R(7)
R256_8_rounds(7);
#endif
#if R256_Unroll_R(8)
R256_8_rounds(8);
#endif
#if R256_Unroll_R(9)
R256_8_rounds(9);
#endif
#if R256_Unroll_R(10)
R256_8_rounds(10);
#endif
#if R256_Unroll_R(11)
R256_8_rounds(11);
#endif
#if R256_Unroll_R(12)
R256_8_rounds(12);
#endif
#if R256_Unroll_R(13)
R256_8_rounds(13);
#endif
#if R256_Unroll_R(14)
R256_8_rounds(14);
#endif
#if (SKEIN_UNROLL_256 > 14)
#error "need more unrolling in Skein_256_Process_Block"
#endif
}
/*
* do the final "feedforward" xor, update context chaining vars
*/
ctx->X[0] = X0 ^ w[0];
ctx->X[1] = X1 ^ w[1];
ctx->X[2] = X2 ^ w[2];
ctx->X[3] = X3 ^ w[3];
Skein_Show_Round(BLK_BITS, &ctx->h, SKEIN_RND_FEED_FWD, ctx->X);
ts[1] &= ~SKEIN_T1_FLAG_FIRST;
}
while (--blkCnt);
ctx->h.T[0] = ts[0];
ctx->h.T[1] = ts[1];
}
#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
size_t
Skein_256_Process_Block_CodeSize(void)
{
return ((uint8_t *)Skein_256_Process_Block_CodeSize) -
((uint8_t *)Skein_256_Process_Block);
}
uint_t
Skein_256_Unroll_Cnt(void)
{
return (SKEIN_UNROLL_256);
}
#endif
#endif
/* Skein_512 */
#if !(SKEIN_USE_ASM & 512)
void
Skein_512_Process_Block(Skein_512_Ctxt_t *ctx, const uint8_t *blkPtr,
size_t blkCnt, size_t byteCntAdd)
{ /* do it in C */
enum {
WCNT = SKEIN_512_STATE_WORDS
};
#undef RCNT
#define RCNT (SKEIN_512_ROUNDS_TOTAL / 8)
#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
#define SKEIN_UNROLL_512 (((SKEIN_LOOP) / 10) % 10)
#else
#define SKEIN_UNROLL_512 (0)
#endif
#if SKEIN_UNROLL_512
#if (RCNT % SKEIN_UNROLL_512)
#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */
#endif
size_t r;
/* key schedule words : chaining vars + tweak + "rotation" */
uint64_t kw[WCNT + 4 + RCNT * 2];
#else
uint64_t kw[WCNT + 4]; /* key schedule words : chaining vars + tweak */
#endif
/* local copy of vars, for speed */
uint64_t X0, X1, X2, X3, X4, X5, X6, X7;
uint64_t w[WCNT]; /* local copy of input block */
#ifdef SKEIN_DEBUG
/* use for debugging (help compiler put Xn in registers) */
const uint64_t *Xptr[8];
Xptr[0] = &X0;
Xptr[1] = &X1;
Xptr[2] = &X2;
Xptr[3] = &X3;
Xptr[4] = &X4;
Xptr[5] = &X5;
Xptr[6] = &X6;
Xptr[7] = &X7;
#endif
Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */
ts[0] = ctx->h.T[0];
ts[1] = ctx->h.T[1];
do {
/*
* this implementation only supports 2**64 input bytes
* (no carry out here)
*/
ts[0] += byteCntAdd; /* update processed length */
/* precompute the key schedule for this block */
ks[0] = ctx->X[0];
ks[1] = ctx->X[1];
ks[2] = ctx->X[2];
ks[3] = ctx->X[3];
ks[4] = ctx->X[4];
ks[5] = ctx->X[5];
ks[6] = ctx->X[6];
ks[7] = ctx->X[7];
ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^
ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY;
ts[2] = ts[0] ^ ts[1];
/* get input block in little-endian format */
Skein_Get64_LSB_First(w, blkPtr, WCNT);
DebugSaveTweak(ctx);
Skein_Show_Block(BLK_BITS, &ctx->h, ctx->X, blkPtr, w, ks, ts);
X0 = w[0] + ks[0]; /* do the first full key injection */
X1 = w[1] + ks[1];
X2 = w[2] + ks[2];
X3 = w[3] + ks[3];
X4 = w[4] + ks[4];
X5 = w[5] + ks[5] + ts[0];
X6 = w[6] + ks[6] + ts[1];
X7 = w[7] + ks[7];
blkPtr += SKEIN_512_BLOCK_BYTES;
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INITIAL,
Xptr);
/* run the rounds */
#define Round512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, rNum) \
X##p0 += X##p1; X##p1 = RotL_64(X##p1, ROT##_0); X##p1 ^= X##p0;\
X##p2 += X##p3; X##p3 = RotL_64(X##p3, ROT##_1); X##p3 ^= X##p2;\
X##p4 += X##p5; X##p5 = RotL_64(X##p5, ROT##_2); X##p5 ^= X##p4;\
X##p6 += X##p7; X##p7 = RotL_64(X##p7, ROT##_3); X##p7 ^= X##p6;
#if SKEIN_UNROLL_512 == 0
#define R512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, rNum) /* unrolled */ \
Round512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, rNum) \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, rNum, Xptr);
#define I512(R) \
X0 += ks[((R) + 1) % 9]; /* inject the key schedule value */\
X1 += ks[((R) + 2) % 9]; \
X2 += ks[((R) + 3) % 9]; \
X3 += ks[((R) + 4) % 9]; \
X4 += ks[((R) + 5) % 9]; \
X5 += ks[((R) + 6) % 9] + ts[((R) + 1) % 3]; \
X6 += ks[((R) + 7) % 9] + ts[((R) + 2) % 3]; \
X7 += ks[((R) + 8) % 9] + (R) + 1; \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INJECT, Xptr);
#else /* looping version */
#define R512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, rNum) \
Round512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, rNum) \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, 4 * (r - 1) + rNum, Xptr);
#define I512(R) \
X0 += ks[r + (R) + 0]; /* inject the key schedule value */ \
X1 += ks[r + (R) + 1]; \
X2 += ks[r + (R) + 2]; \
X3 += ks[r + (R) + 3]; \
X4 += ks[r + (R) + 4]; \
X5 += ks[r + (R) + 5] + ts[r + (R) + 0]; \
X6 += ks[r + (R) + 6] + ts[r + (R) + 1]; \
X7 += ks[r + (R) + 7] + r + (R); \
ks[r + (R)+8] = ks[r + (R) - 1]; /* rotate key schedule */\
ts[r + (R)+2] = ts[r + (R) - 1]; \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INJECT, Xptr);
/* loop thru it */
for (r = 1; r < 2 * RCNT; r += 2 * SKEIN_UNROLL_512)
#endif /* end of looped code definitions */
{
#define R512_8_rounds(R) /* do 8 full rounds */ \
R512(0, 1, 2, 3, 4, 5, 6, 7, R_512_0, 8 * (R) + 1); \
R512(2, 1, 4, 7, 6, 5, 0, 3, R_512_1, 8 * (R) + 2); \
R512(4, 1, 6, 3, 0, 5, 2, 7, R_512_2, 8 * (R) + 3); \
R512(6, 1, 0, 7, 2, 5, 4, 3, R_512_3, 8 * (R) + 4); \
I512(2 * (R)); \
R512(0, 1, 2, 3, 4, 5, 6, 7, R_512_4, 8 * (R) + 5); \
R512(2, 1, 4, 7, 6, 5, 0, 3, R_512_5, 8 * (R) + 6); \
R512(4, 1, 6, 3, 0, 5, 2, 7, R_512_6, 8 * (R) + 7); \
R512(6, 1, 0, 7, 2, 5, 4, 3, R_512_7, 8 * (R) + 8); \
I512(2*(R) + 1); /* and key injection */
R512_8_rounds(0);
#define R512_Unroll_R(NN) \
((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL / 8 > (NN)) || \
(SKEIN_UNROLL_512 > (NN)))
#if R512_Unroll_R(1)
R512_8_rounds(1);
#endif
#if R512_Unroll_R(2)
R512_8_rounds(2);
#endif
#if R512_Unroll_R(3)
R512_8_rounds(3);
#endif
#if R512_Unroll_R(4)
R512_8_rounds(4);
#endif
#if R512_Unroll_R(5)
R512_8_rounds(5);
#endif
#if R512_Unroll_R(6)
R512_8_rounds(6);
#endif
#if R512_Unroll_R(7)
R512_8_rounds(7);
#endif
#if R512_Unroll_R(8)
R512_8_rounds(8);
#endif
#if R512_Unroll_R(9)
R512_8_rounds(9);
#endif
#if R512_Unroll_R(10)
R512_8_rounds(10);
#endif
#if R512_Unroll_R(11)
R512_8_rounds(11);
#endif
#if R512_Unroll_R(12)
R512_8_rounds(12);
#endif
#if R512_Unroll_R(13)
R512_8_rounds(13);
#endif
#if R512_Unroll_R(14)
R512_8_rounds(14);
#endif
#if (SKEIN_UNROLL_512 > 14)
#error "need more unrolling in Skein_512_Process_Block"
#endif
}
/*
* do the final "feedforward" xor, update context chaining vars
*/
ctx->X[0] = X0 ^ w[0];
ctx->X[1] = X1 ^ w[1];
ctx->X[2] = X2 ^ w[2];
ctx->X[3] = X3 ^ w[3];
ctx->X[4] = X4 ^ w[4];
ctx->X[5] = X5 ^ w[5];
ctx->X[6] = X6 ^ w[6];
ctx->X[7] = X7 ^ w[7];
Skein_Show_Round(BLK_BITS, &ctx->h, SKEIN_RND_FEED_FWD, ctx->X);
ts[1] &= ~SKEIN_T1_FLAG_FIRST;
}
while (--blkCnt);
ctx->h.T[0] = ts[0];
ctx->h.T[1] = ts[1];
}
#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
size_t
Skein_512_Process_Block_CodeSize(void)
{
return ((uint8_t *)Skein_512_Process_Block_CodeSize) -
((uint8_t *)Skein_512_Process_Block);
}
uint_t
Skein_512_Unroll_Cnt(void)
{
return (SKEIN_UNROLL_512);
}
#endif
#endif
/* Skein1024 */
#if !(SKEIN_USE_ASM & 1024)
void
Skein1024_Process_Block(Skein1024_Ctxt_t *ctx, const uint8_t *blkPtr,
size_t blkCnt, size_t byteCntAdd)
{
/* do it in C, always looping (unrolled is bigger AND slower!) */
enum {
WCNT = SKEIN1024_STATE_WORDS
};
#undef RCNT
#define RCNT (SKEIN1024_ROUNDS_TOTAL/8)
#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10)
#else
#define SKEIN_UNROLL_1024 (0)
#endif
#if (SKEIN_UNROLL_1024 != 0)
#if (RCNT % SKEIN_UNROLL_1024)
#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */
#endif
size_t r;
/* key schedule words : chaining vars + tweak + "rotation" */
uint64_t kw[WCNT + 4 + RCNT * 2];
#else
uint64_t kw[WCNT + 4]; /* key schedule words : chaining vars + tweak */
#endif
/* local copy of vars, for speed */
uint64_t X00, X01, X02, X03, X04, X05, X06, X07, X08, X09, X10, X11,
X12, X13, X14, X15;
uint64_t w[WCNT]; /* local copy of input block */
#ifdef SKEIN_DEBUG
/* use for debugging (help compiler put Xn in registers) */
const uint64_t *Xptr[16];
Xptr[0] = &X00;
Xptr[1] = &X01;
Xptr[2] = &X02;
Xptr[3] = &X03;
Xptr[4] = &X04;
Xptr[5] = &X05;
Xptr[6] = &X06;
Xptr[7] = &X07;
Xptr[8] = &X08;
Xptr[9] = &X09;
Xptr[10] = &X10;
Xptr[11] = &X11;
Xptr[12] = &X12;
Xptr[13] = &X13;
Xptr[14] = &X14;
Xptr[15] = &X15;
#endif
Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */
ts[0] = ctx->h.T[0];
ts[1] = ctx->h.T[1];
do {
/*
* this implementation only supports 2**64 input bytes
* (no carry out here)
*/
ts[0] += byteCntAdd; /* update processed length */
/* precompute the key schedule for this block */
ks[0] = ctx->X[0];
ks[1] = ctx->X[1];
ks[2] = ctx->X[2];
ks[3] = ctx->X[3];
ks[4] = ctx->X[4];
ks[5] = ctx->X[5];
ks[6] = ctx->X[6];
ks[7] = ctx->X[7];
ks[8] = ctx->X[8];
ks[9] = ctx->X[9];
ks[10] = ctx->X[10];
ks[11] = ctx->X[11];
ks[12] = ctx->X[12];
ks[13] = ctx->X[13];
ks[14] = ctx->X[14];
ks[15] = ctx->X[15];
ks[16] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^
ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^
ks[8] ^ ks[9] ^ ks[10] ^ ks[11] ^
ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY;
ts[2] = ts[0] ^ ts[1];
/* get input block in little-endian format */
Skein_Get64_LSB_First(w, blkPtr, WCNT);
DebugSaveTweak(ctx);
Skein_Show_Block(BLK_BITS, &ctx->h, ctx->X, blkPtr, w, ks, ts);
X00 = w[0] + ks[0]; /* do the first full key injection */
X01 = w[1] + ks[1];
X02 = w[2] + ks[2];
X03 = w[3] + ks[3];
X04 = w[4] + ks[4];
X05 = w[5] + ks[5];
X06 = w[6] + ks[6];
X07 = w[7] + ks[7];
X08 = w[8] + ks[8];
X09 = w[9] + ks[9];
X10 = w[10] + ks[10];
X11 = w[11] + ks[11];
X12 = w[12] + ks[12];
X13 = w[13] + ks[13] + ts[0];
X14 = w[14] + ks[14] + ts[1];
X15 = w[15] + ks[15];
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INITIAL,
Xptr);
#define Round1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, \
pD, pE, pF, ROT, rNum) \
X##p0 += X##p1; X##p1 = RotL_64(X##p1, ROT##_0); X##p1 ^= X##p0;\
X##p2 += X##p3; X##p3 = RotL_64(X##p3, ROT##_1); X##p3 ^= X##p2;\
X##p4 += X##p5; X##p5 = RotL_64(X##p5, ROT##_2); X##p5 ^= X##p4;\
X##p6 += X##p7; X##p7 = RotL_64(X##p7, ROT##_3); X##p7 ^= X##p6;\
X##p8 += X##p9; X##p9 = RotL_64(X##p9, ROT##_4); X##p9 ^= X##p8;\
X##pA += X##pB; X##pB = RotL_64(X##pB, ROT##_5); X##pB ^= X##pA;\
X##pC += X##pD; X##pD = RotL_64(X##pD, ROT##_6); X##pD ^= X##pC;\
X##pE += X##pF; X##pF = RotL_64(X##pF, ROT##_7); X##pF ^= X##pE;
#if SKEIN_UNROLL_1024 == 0
#define R1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, \
pE, pF, ROT, rn) \
Round1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, \
pD, pE, pF, ROT, rn) \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, rn, Xptr);
#define I1024(R) \
X00 += ks[((R) + 1) % 17]; /* inject the key schedule value */\
X01 += ks[((R) + 2) % 17]; \
X02 += ks[((R) + 3) % 17]; \
X03 += ks[((R) + 4) % 17]; \
X04 += ks[((R) + 5) % 17]; \
X05 += ks[((R) + 6) % 17]; \
X06 += ks[((R) + 7) % 17]; \
X07 += ks[((R) + 8) % 17]; \
X08 += ks[((R) + 9) % 17]; \
X09 += ks[((R) + 10) % 17]; \
X10 += ks[((R) + 11) % 17]; \
X11 += ks[((R) + 12) % 17]; \
X12 += ks[((R) + 13) % 17]; \
X13 += ks[((R) + 14) % 17] + ts[((R) + 1) % 3]; \
X14 += ks[((R) + 15) % 17] + ts[((R) + 2) % 3]; \
X15 += ks[((R) + 16) % 17] + (R) +1; \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INJECT, Xptr);
#else /* looping version */
#define R1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, \
pE, pF, ROT, rn) \
Round1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, \
pD, pE, pF, ROT, rn) \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, 4 * (r - 1) + rn, Xptr);
#define I1024(R) \
X00 += ks[r + (R) + 0]; /* inject the key schedule value */ \
X01 += ks[r + (R) + 1]; \
X02 += ks[r + (R) + 2]; \
X03 += ks[r + (R) + 3]; \
X04 += ks[r + (R) + 4]; \
X05 += ks[r + (R) + 5]; \
X06 += ks[r + (R) + 6]; \
X07 += ks[r + (R) + 7]; \
X08 += ks[r + (R) + 8]; \
X09 += ks[r + (R) + 9]; \
X10 += ks[r + (R) + 10]; \
X11 += ks[r + (R) + 11]; \
X12 += ks[r + (R) + 12]; \
X13 += ks[r + (R) + 13] + ts[r + (R) + 0]; \
X14 += ks[r + (R) + 14] + ts[r + (R) + 1]; \
X15 += ks[r + (R) + 15] + r + (R); \
ks[r + (R) + 16] = ks[r + (R) - 1]; /* rotate key schedule */\
ts[r + (R) + 2] = ts[r + (R) - 1]; \
Skein_Show_R_Ptr(BLK_BITS, &ctx->h, SKEIN_RND_KEY_INJECT, Xptr);
/* loop thru it */
for (r = 1; r <= 2 * RCNT; r += 2 * SKEIN_UNROLL_1024)
#endif
{
#define R1024_8_rounds(R) /* do 8 full rounds */ \
R1024(00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, \
14, 15, R1024_0, 8 * (R) + 1); \
R1024(00, 09, 02, 13, 06, 11, 04, 15, 10, 07, 12, 03, 14, 05, \
08, 01, R1024_1, 8 * (R) + 2); \
R1024(00, 07, 02, 05, 04, 03, 06, 01, 12, 15, 14, 13, 08, 11, \
10, 09, R1024_2, 8 * (R) + 3); \
R1024(00, 15, 02, 11, 06, 13, 04, 09, 14, 01, 08, 05, 10, 03, \
12, 07, R1024_3, 8 * (R) + 4); \
I1024(2 * (R)); \
R1024(00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, \
14, 15, R1024_4, 8 * (R) + 5); \
R1024(00, 09, 02, 13, 06, 11, 04, 15, 10, 07, 12, 03, 14, 05, \
08, 01, R1024_5, 8 * (R) + 6); \
R1024(00, 07, 02, 05, 04, 03, 06, 01, 12, 15, 14, 13, 08, 11, \
10, 09, R1024_6, 8 * (R) + 7); \
R1024(00, 15, 02, 11, 06, 13, 04, 09, 14, 01, 08, 05, 10, 03, \
12, 07, R1024_7, 8 * (R) + 8); \
I1024(2 * (R) + 1);
R1024_8_rounds(0);
#define R1024_Unroll_R(NN) \
((SKEIN_UNROLL_1024 == 0 && SKEIN1024_ROUNDS_TOTAL/8 > (NN)) || \
(SKEIN_UNROLL_1024 > (NN)))
#if R1024_Unroll_R(1)
R1024_8_rounds(1);
#endif
#if R1024_Unroll_R(2)
R1024_8_rounds(2);
#endif
#if R1024_Unroll_R(3)
R1024_8_rounds(3);
#endif
#if R1024_Unroll_R(4)
R1024_8_rounds(4);
#endif
#if R1024_Unroll_R(5)
R1024_8_rounds(5);
#endif
#if R1024_Unroll_R(6)
R1024_8_rounds(6);
#endif
#if R1024_Unroll_R(7)
R1024_8_rounds(7);
#endif
#if R1024_Unroll_R(8)
R1024_8_rounds(8);
#endif
#if R1024_Unroll_R(9)
R1024_8_rounds(9);
#endif
#if R1024_Unroll_R(10)
R1024_8_rounds(10);
#endif
#if R1024_Unroll_R(11)
R1024_8_rounds(11);
#endif
#if R1024_Unroll_R(12)
R1024_8_rounds(12);
#endif
#if R1024_Unroll_R(13)
R1024_8_rounds(13);
#endif
#if R1024_Unroll_R(14)
R1024_8_rounds(14);
#endif
#if (SKEIN_UNROLL_1024 > 14)
#error "need more unrolling in Skein_1024_Process_Block"
#endif
}
/*
* do the final "feedforward" xor, update context chaining vars
*/
ctx->X[0] = X00 ^ w[0];
ctx->X[1] = X01 ^ w[1];
ctx->X[2] = X02 ^ w[2];
ctx->X[3] = X03 ^ w[3];
ctx->X[4] = X04 ^ w[4];
ctx->X[5] = X05 ^ w[5];
ctx->X[6] = X06 ^ w[6];
ctx->X[7] = X07 ^ w[7];
ctx->X[8] = X08 ^ w[8];
ctx->X[9] = X09 ^ w[9];
ctx->X[10] = X10 ^ w[10];
ctx->X[11] = X11 ^ w[11];
ctx->X[12] = X12 ^ w[12];
ctx->X[13] = X13 ^ w[13];
ctx->X[14] = X14 ^ w[14];
ctx->X[15] = X15 ^ w[15];
Skein_Show_Round(BLK_BITS, &ctx->h, SKEIN_RND_FEED_FWD, ctx->X);
ts[1] &= ~SKEIN_T1_FLAG_FIRST;
blkPtr += SKEIN1024_BLOCK_BYTES;
} while (--blkCnt);
ctx->h.T[0] = ts[0];
ctx->h.T[1] = ts[1];
}
#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
size_t
Skein1024_Process_Block_CodeSize(void)
{
return ((uint8_t *)Skein1024_Process_Block_CodeSize) -
((uint8_t *)Skein1024_Process_Block);
}
uint_t
Skein1024_Unroll_Cnt(void)
{
return (SKEIN_UNROLL_1024);
}
#endif
#endif

View File

@ -0,0 +1,289 @@
/*
* Internal definitions for Skein hashing.
* Source code author: Doug Whiting, 2008.
* This algorithm and source code is released to the public domain.
*
* The following compile-time switches may be defined to control some
* tradeoffs between speed, code size, error checking, and security.
*
* The "default" note explains what happens when the switch is not defined.
*
* SKEIN_DEBUG -- make callouts from inside Skein code
* to examine/display intermediate values.
* [default: no callouts (no overhead)]
*
* SKEIN_ERR_CHECK -- how error checking is handled inside Skein
* code. If not defined, most error checking
* is disabled (for performance). Otherwise,
* the switch value is interpreted as:
* 0: use assert() to flag errors
* 1: return SKEIN_FAIL to flag errors
*/
/* Copyright 2013 Doug Whiting. This code is released to the public domain. */
#ifndef _SKEIN_IMPL_H_
#define _SKEIN_IMPL_H_
#include <sys/skein.h>
#include "skein_impl.h"
#include "skein_port.h"
/* determine where we can get bcopy/bzero declarations */
#ifdef _KERNEL
#include <sys/systm.h>
#else
#include <strings.h>
#endif
/*
* "Internal" Skein definitions
* -- not needed for sequential hashing API, but will be
* helpful for other uses of Skein (e.g., tree hash mode).
* -- included here so that they can be shared between
* reference and optimized code.
*/
/* tweak word T[1]: bit field starting positions */
/* offset 64 because it's the second word */
#define SKEIN_T1_BIT(BIT) ((BIT) - 64)
/* bits 112..118: level in hash tree */
#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112)
/* bit 119: partial final input byte */
#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119)
/* bits 120..125: type field */
#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120)
/* bits 126: first block flag */
#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126)
/* bit 127: final block flag */
#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127)
/* tweak word T[1]: flag bit definition(s) */
#define SKEIN_T1_FLAG_FIRST (((uint64_t)1) << SKEIN_T1_POS_FIRST)
#define SKEIN_T1_FLAG_FINAL (((uint64_t)1) << SKEIN_T1_POS_FINAL)
#define SKEIN_T1_FLAG_BIT_PAD (((uint64_t)1) << SKEIN_T1_POS_BIT_PAD)
/* tweak word T[1]: tree level bit field mask */
#define SKEIN_T1_TREE_LVL_MASK (((uint64_t)0x7F) << SKEIN_T1_POS_TREE_LVL)
#define SKEIN_T1_TREE_LEVEL(n) (((uint64_t)(n)) << SKEIN_T1_POS_TREE_LVL)
/* tweak word T[1]: block type field */
#define SKEIN_BLK_TYPE_KEY (0) /* key, for MAC and KDF */
#define SKEIN_BLK_TYPE_CFG (4) /* configuration block */
#define SKEIN_BLK_TYPE_PERS (8) /* personalization string */
#define SKEIN_BLK_TYPE_PK (12) /* public key (for signature hashing) */
#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */
#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */
#define SKEIN_BLK_TYPE_MSG (48) /* message processing */
#define SKEIN_BLK_TYPE_OUT (63) /* output stage */
#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */
#define SKEIN_T1_BLK_TYPE(T) \
(((uint64_t)(SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE)
/* key, for MAC and KDF */
#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY)
/* configuration block */
#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG)
/* personalization string */
#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS)
/* public key (for digital signature hashing) */
#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK)
/* key identifier for KDF */
#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF)
/* nonce for PRNG */
#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)
/* message processing */
#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG)
/* output stage */
#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT)
/* field bit mask */
#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK)
#define SKEIN_T1_BLK_TYPE_CFG_FINAL \
(SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL)
#define SKEIN_T1_BLK_TYPE_OUT_FINAL \
(SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL)
#define SKEIN_VERSION (1)
#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */
#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian) */
#endif
#define SKEIN_MK_64(hi32, lo32) ((lo32) + (((uint64_t)(hi32)) << 32))
#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION, SKEIN_ID_STRING_LE)
#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA, 0xA9FC1A22)
#define SKEIN_CFG_STR_LEN (4*8)
/* bit field definitions in config block treeInfo word */
#define SKEIN_CFG_TREE_LEAF_SIZE_POS (0)
#define SKEIN_CFG_TREE_NODE_SIZE_POS (8)
#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16)
#define SKEIN_CFG_TREE_LEAF_SIZE_MSK \
(((uint64_t)0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS)
#define SKEIN_CFG_TREE_NODE_SIZE_MSK \
(((uint64_t)0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS)
#define SKEIN_CFG_TREE_MAX_LEVEL_MSK \
(((uint64_t)0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS)
#define SKEIN_CFG_TREE_INFO(leaf, node, maxLvl) \
((((uint64_t)(leaf)) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \
(((uint64_t)(node)) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \
(((uint64_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS))
/* use as treeInfo in InitExt() call for sequential processing */
#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0, 0, 0)
/*
* Skein macros for getting/setting tweak words, etc.
* These are useful for partial input bytes, hash tree init/update, etc.
*/
#define Skein_Get_Tweak(ctxPtr, TWK_NUM) ((ctxPtr)->h.T[TWK_NUM])
#define Skein_Set_Tweak(ctxPtr, TWK_NUM, tVal) \
do { \
(ctxPtr)->h.T[TWK_NUM] = (tVal); \
_NOTE(CONSTCOND) \
} while (0)
#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr, 0)
#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr, 1)
#define Skein_Set_T0(ctxPtr, T0) Skein_Set_Tweak(ctxPtr, 0, T0)
#define Skein_Set_T1(ctxPtr, T1) Skein_Set_Tweak(ctxPtr, 1, T1)
/* set both tweak words at once */
#define Skein_Set_T0_T1(ctxPtr, T0, T1) \
do { \
Skein_Set_T0(ctxPtr, (T0)); \
Skein_Set_T1(ctxPtr, (T1)); \
_NOTE(CONSTCOND) \
} while (0)
#define Skein_Set_Type(ctxPtr, BLK_TYPE) \
Skein_Set_T1(ctxPtr, SKEIN_T1_BLK_TYPE_##BLK_TYPE)
/*
* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0;
*/
#define Skein_Start_New_Type(ctxPtr, BLK_TYPE) \
do { \
Skein_Set_T0_T1(ctxPtr, 0, SKEIN_T1_FLAG_FIRST | \
SKEIN_T1_BLK_TYPE_ ## BLK_TYPE); \
(ctxPtr)->h.bCnt = 0; \
_NOTE(CONSTCOND) \
} while (0)
#define Skein_Clear_First_Flag(hdr) \
do { \
(hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; \
_NOTE(CONSTCOND) \
} while (0)
#define Skein_Set_Bit_Pad_Flag(hdr) \
do { \
(hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; \
_NOTE(CONSTCOND) \
} while (0)
#define Skein_Set_Tree_Level(hdr, height) \
do { \
(hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height); \
_NOTE(CONSTCOND) \
} while (0)
/*
* "Internal" Skein definitions for debugging and error checking
* Note: in Illumos we always disable debugging features.
*/
#define Skein_Show_Block(bits, ctx, X, blkPtr, wPtr, ksEvenPtr, ksOddPtr)
#define Skein_Show_Round(bits, ctx, r, X)
#define Skein_Show_R_Ptr(bits, ctx, r, X_ptr)
#define Skein_Show_Final(bits, ctx, cnt, outPtr)
#define Skein_Show_Key(bits, ctx, key, keyBytes)
/* run-time checks (e.g., bad params, uninitialized context)? */
#ifndef SKEIN_ERR_CHECK
/* default: ignore all Asserts, for performance */
#define Skein_Assert(x, retCode)
#define Skein_assert(x)
#elif defined(SKEIN_ASSERT)
#include <sys/debug.h>
#define Skein_Assert(x, retCode) ASSERT(x)
#define Skein_assert(x) ASSERT(x)
#else
#include <sys/debug.h>
/* caller error */
#define Skein_Assert(x, retCode) \
do { \
if (!(x)) \
return (retCode); \
_NOTE(CONSTCOND) \
} while (0)
/* internal error */
#define Skein_assert(x) ASSERT(x)
#endif
/*
* Skein block function constants (shared across Ref and Opt code)
*/
enum {
/* Skein_256 round rotation constants */
R_256_0_0 = 14, R_256_0_1 = 16,
R_256_1_0 = 52, R_256_1_1 = 57,
R_256_2_0 = 23, R_256_2_1 = 40,
R_256_3_0 = 5, R_256_3_1 = 37,
R_256_4_0 = 25, R_256_4_1 = 33,
R_256_5_0 = 46, R_256_5_1 = 12,
R_256_6_0 = 58, R_256_6_1 = 22,
R_256_7_0 = 32, R_256_7_1 = 32,
/* Skein_512 round rotation constants */
R_512_0_0 = 46, R_512_0_1 = 36, R_512_0_2 = 19, R_512_0_3 = 37,
R_512_1_0 = 33, R_512_1_1 = 27, R_512_1_2 = 14, R_512_1_3 = 42,
R_512_2_0 = 17, R_512_2_1 = 49, R_512_2_2 = 36, R_512_2_3 = 39,
R_512_3_0 = 44, R_512_3_1 = 9, R_512_3_2 = 54, R_512_3_3 = 56,
R_512_4_0 = 39, R_512_4_1 = 30, R_512_4_2 = 34, R_512_4_3 = 24,
R_512_5_0 = 13, R_512_5_1 = 50, R_512_5_2 = 10, R_512_5_3 = 17,
R_512_6_0 = 25, R_512_6_1 = 29, R_512_6_2 = 39, R_512_6_3 = 43,
R_512_7_0 = 8, R_512_7_1 = 35, R_512_7_2 = 56, R_512_7_3 = 22,
/* Skein1024 round rotation constants */
R1024_0_0 = 24, R1024_0_1 = 13, R1024_0_2 = 8, R1024_0_3 =
47, R1024_0_4 = 8, R1024_0_5 = 17, R1024_0_6 = 22, R1024_0_7 = 37,
R1024_1_0 = 38, R1024_1_1 = 19, R1024_1_2 = 10, R1024_1_3 =
55, R1024_1_4 = 49, R1024_1_5 = 18, R1024_1_6 = 23, R1024_1_7 = 52,
R1024_2_0 = 33, R1024_2_1 = 4, R1024_2_2 = 51, R1024_2_3 =
13, R1024_2_4 = 34, R1024_2_5 = 41, R1024_2_6 = 59, R1024_2_7 = 17,
R1024_3_0 = 5, R1024_3_1 = 20, R1024_3_2 = 48, R1024_3_3 =
41, R1024_3_4 = 47, R1024_3_5 = 28, R1024_3_6 = 16, R1024_3_7 = 25,
R1024_4_0 = 41, R1024_4_1 = 9, R1024_4_2 = 37, R1024_4_3 =
31, R1024_4_4 = 12, R1024_4_5 = 47, R1024_4_6 = 44, R1024_4_7 = 30,
R1024_5_0 = 16, R1024_5_1 = 34, R1024_5_2 = 56, R1024_5_3 =
51, R1024_5_4 = 4, R1024_5_5 = 53, R1024_5_6 = 42, R1024_5_7 = 41,
R1024_6_0 = 31, R1024_6_1 = 44, R1024_6_2 = 47, R1024_6_3 =
46, R1024_6_4 = 19, R1024_6_5 = 42, R1024_6_6 = 44, R1024_6_7 = 25,
R1024_7_0 = 9, R1024_7_1 = 48, R1024_7_2 = 35, R1024_7_3 =
52, R1024_7_4 = 23, R1024_7_5 = 31, R1024_7_6 = 37, R1024_7_7 = 20
};
/* number of rounds for the different block sizes */
#define SKEIN_256_ROUNDS_TOTAL (72)
#define SKEIN_512_ROUNDS_TOTAL (72)
#define SKEIN1024_ROUNDS_TOTAL (80)
extern const uint64_t SKEIN_256_IV_128[];
extern const uint64_t SKEIN_256_IV_160[];
extern const uint64_t SKEIN_256_IV_224[];
extern const uint64_t SKEIN_256_IV_256[];
extern const uint64_t SKEIN_512_IV_128[];
extern const uint64_t SKEIN_512_IV_160[];
extern const uint64_t SKEIN_512_IV_224[];
extern const uint64_t SKEIN_512_IV_256[];
extern const uint64_t SKEIN_512_IV_384[];
extern const uint64_t SKEIN_512_IV_512[];
extern const uint64_t SKEIN1024_IV_384[];
extern const uint64_t SKEIN1024_IV_512[];
extern const uint64_t SKEIN1024_IV_1024[];
#endif /* _SKEIN_IMPL_H_ */

View File

@ -0,0 +1,185 @@
/*
* Pre-computed Skein IVs
*
* NOTE: these values are not "magic" constants, but
* are generated using the Threefish block function.
* They are pre-computed here only for speed; i.e., to
* avoid the need for a Threefish call during Init().
*
* The IV for any fixed hash length may be pre-computed.
* Only the most common values are included here.
*/
/* Copyright 2013 Doug Whiting. This code is released to the public domain. */
/*
* Illumos implementation note: these constants are for Skein v1.3 as per:
* http://www.skein-hash.info/sites/default/files/skein1.3.pdf
*/
#include <sys/skein.h> /* get Skein macros and types */
#include "skein_impl.h" /* get internal definitions */
#define MK_64 SKEIN_MK_64
/* blkSize = 256 bits. hashSize = 128 bits */
const uint64_t SKEIN_256_IV_128[] = {
MK_64(0xE1111906, 0x964D7260),
MK_64(0x883DAAA7, 0x7C8D811C),
MK_64(0x10080DF4, 0x91960F7A),
MK_64(0xCCF7DDE5, 0xB45BC1C2)
};
/* blkSize = 256 bits. hashSize = 160 bits */
const uint64_t SKEIN_256_IV_160[] = {
MK_64(0x14202314, 0x72825E98),
MK_64(0x2AC4E9A2, 0x5A77E590),
MK_64(0xD47A5856, 0x8838D63E),
MK_64(0x2DD2E496, 0x8586AB7D)
};
/* blkSize = 256 bits. hashSize = 224 bits */
const uint64_t SKEIN_256_IV_224[] = {
MK_64(0xC6098A8C, 0x9AE5EA0B),
MK_64(0x876D5686, 0x08C5191C),
MK_64(0x99CB88D7, 0xD7F53884),
MK_64(0x384BDDB1, 0xAEDDB5DE)
};
/* blkSize = 256 bits. hashSize = 256 bits */
const uint64_t SKEIN_256_IV_256[] = {
MK_64(0xFC9DA860, 0xD048B449),
MK_64(0x2FCA6647, 0x9FA7D833),
MK_64(0xB33BC389, 0x6656840F),
MK_64(0x6A54E920, 0xFDE8DA69)
};
/* blkSize = 512 bits. hashSize = 128 bits */
const uint64_t SKEIN_512_IV_128[] = {
MK_64(0xA8BC7BF3, 0x6FBF9F52),
MK_64(0x1E9872CE, 0xBD1AF0AA),
MK_64(0x309B1790, 0xB32190D3),
MK_64(0xBCFBB854, 0x3F94805C),
MK_64(0x0DA61BCD, 0x6E31B11B),
MK_64(0x1A18EBEA, 0xD46A32E3),
MK_64(0xA2CC5B18, 0xCE84AA82),
MK_64(0x6982AB28, 0x9D46982D)
};
/* blkSize = 512 bits. hashSize = 160 bits */
const uint64_t SKEIN_512_IV_160[] = {
MK_64(0x28B81A2A, 0xE013BD91),
MK_64(0xC2F11668, 0xB5BDF78F),
MK_64(0x1760D8F3, 0xF6A56F12),
MK_64(0x4FB74758, 0x8239904F),
MK_64(0x21EDE07F, 0x7EAF5056),
MK_64(0xD908922E, 0x63ED70B8),
MK_64(0xB8EC76FF, 0xECCB52FA),
MK_64(0x01A47BB8, 0xA3F27A6E)
};
/* blkSize = 512 bits. hashSize = 224 bits */
const uint64_t SKEIN_512_IV_224[] = {
MK_64(0xCCD06162, 0x48677224),
MK_64(0xCBA65CF3, 0xA92339EF),
MK_64(0x8CCD69D6, 0x52FF4B64),
MK_64(0x398AED7B, 0x3AB890B4),
MK_64(0x0F59D1B1, 0x457D2BD0),
MK_64(0x6776FE65, 0x75D4EB3D),
MK_64(0x99FBC70E, 0x997413E9),
MK_64(0x9E2CFCCF, 0xE1C41EF7)
};
/* blkSize = 512 bits. hashSize = 256 bits */
const uint64_t SKEIN_512_IV_256[] = {
MK_64(0xCCD044A1, 0x2FDB3E13),
MK_64(0xE8359030, 0x1A79A9EB),
MK_64(0x55AEA061, 0x4F816E6F),
MK_64(0x2A2767A4, 0xAE9B94DB),
MK_64(0xEC06025E, 0x74DD7683),
MK_64(0xE7A436CD, 0xC4746251),
MK_64(0xC36FBAF9, 0x393AD185),
MK_64(0x3EEDBA18, 0x33EDFC13)
};
/* blkSize = 512 bits. hashSize = 384 bits */
const uint64_t SKEIN_512_IV_384[] = {
MK_64(0xA3F6C6BF, 0x3A75EF5F),
MK_64(0xB0FEF9CC, 0xFD84FAA4),
MK_64(0x9D77DD66, 0x3D770CFE),
MK_64(0xD798CBF3, 0xB468FDDA),
MK_64(0x1BC4A666, 0x8A0E4465),
MK_64(0x7ED7D434, 0xE5807407),
MK_64(0x548FC1AC, 0xD4EC44D6),
MK_64(0x266E1754, 0x6AA18FF8)
};
/* blkSize = 512 bits. hashSize = 512 bits */
const uint64_t SKEIN_512_IV_512[] = {
MK_64(0x4903ADFF, 0x749C51CE),
MK_64(0x0D95DE39, 0x9746DF03),
MK_64(0x8FD19341, 0x27C79BCE),
MK_64(0x9A255629, 0xFF352CB1),
MK_64(0x5DB62599, 0xDF6CA7B0),
MK_64(0xEABE394C, 0xA9D5C3F4),
MK_64(0x991112C7, 0x1A75B523),
MK_64(0xAE18A40B, 0x660FCC33)
};
/* blkSize = 1024 bits. hashSize = 384 bits */
const uint64_t SKEIN1024_IV_384[] = {
MK_64(0x5102B6B8, 0xC1894A35),
MK_64(0xFEEBC9E3, 0xFE8AF11A),
MK_64(0x0C807F06, 0xE32BED71),
MK_64(0x60C13A52, 0xB41A91F6),
MK_64(0x9716D35D, 0xD4917C38),
MK_64(0xE780DF12, 0x6FD31D3A),
MK_64(0x797846B6, 0xC898303A),
MK_64(0xB172C2A8, 0xB3572A3B),
MK_64(0xC9BC8203, 0xA6104A6C),
MK_64(0x65909338, 0xD75624F4),
MK_64(0x94BCC568, 0x4B3F81A0),
MK_64(0x3EBBF51E, 0x10ECFD46),
MK_64(0x2DF50F0B, 0xEEB08542),
MK_64(0x3B5A6530, 0x0DBC6516),
MK_64(0x484B9CD2, 0x167BBCE1),
MK_64(0x2D136947, 0xD4CBAFEA)
};
/* blkSize = 1024 bits. hashSize = 512 bits */
const uint64_t SKEIN1024_IV_512[] = {
MK_64(0xCAEC0E5D, 0x7C1B1B18),
MK_64(0xA01B0E04, 0x5F03E802),
MK_64(0x33840451, 0xED912885),
MK_64(0x374AFB04, 0xEAEC2E1C),
MK_64(0xDF25A0E2, 0x813581F7),
MK_64(0xE4004093, 0x8B12F9D2),
MK_64(0xA662D539, 0xC2ED39B6),
MK_64(0xFA8B85CF, 0x45D8C75A),
MK_64(0x8316ED8E, 0x29EDE796),
MK_64(0x053289C0, 0x2E9F91B8),
MK_64(0xC3F8EF1D, 0x6D518B73),
MK_64(0xBDCEC3C4, 0xD5EF332E),
MK_64(0x549A7E52, 0x22974487),
MK_64(0x67070872, 0x5B749816),
MK_64(0xB9CD28FB, 0xF0581BD1),
MK_64(0x0E2940B8, 0x15804974)
};
/* blkSize = 1024 bits. hashSize = 1024 bits */
const uint64_t SKEIN1024_IV_1024[] = {
MK_64(0xD593DA07, 0x41E72355),
MK_64(0x15B5E511, 0xAC73E00C),
MK_64(0x5180E5AE, 0xBAF2C4F0),
MK_64(0x03BD41D3, 0xFCBCAFAF),
MK_64(0x1CAEC6FD, 0x1983A898),
MK_64(0x6E510B8B, 0xCDD0589F),
MK_64(0x77E2BDFD, 0xC6394ADA),
MK_64(0xC11E1DB5, 0x24DCB0A3),
MK_64(0xD6D14AF9, 0xC6329AB5),
MK_64(0x6A9B0BFC, 0x6EB67E0D),
MK_64(0x9243C60D, 0xCCFF1332),
MK_64(0x1A1F1DDE, 0x743F02D4),
MK_64(0x0996753C, 0x10ED0BB8),
MK_64(0x6572DD22, 0xF2B4969A),
MK_64(0x61FD3062, 0xD00A579A),
MK_64(0x1DE0536E, 0x8682E539)
};

View File

@ -0,0 +1,128 @@
/*
* Platform-specific definitions for Skein hash function.
*
* Source code author: Doug Whiting, 2008.
*
* This algorithm and source code is released to the public domain.
*
* Many thanks to Brian Gladman for his portable header files.
*
* To port Skein to an "unsupported" platform, change the definitions
* in this file appropriately.
*/
/* Copyright 2013 Doug Whiting. This code is released to the public domain. */
#ifndef _SKEIN_PORT_H_
#define _SKEIN_PORT_H_
#include <sys/types.h> /* get integer type definitions */
#include <sys/systm.h> /* for bcopy() */
#ifndef RotL_64
#define RotL_64(x, N) (((x) << (N)) | ((x) >> (64 - (N))))
#endif
/*
* Skein is "natively" little-endian (unlike SHA-xxx), for optimal
* performance on x86 CPUs. The Skein code requires the following
* definitions for dealing with endianness:
*
* SKEIN_NEED_SWAP: 0 for little-endian, 1 for big-endian
* Skein_Put64_LSB_First
* Skein_Get64_LSB_First
* Skein_Swap64
*
* If SKEIN_NEED_SWAP is defined at compile time, it is used here
* along with the portable versions of Put64/Get64/Swap64, which
* are slow in general.
*
* Otherwise, an "auto-detect" of endianness is attempted below.
* If the default handling doesn't work well, the user may insert
* platform-specific code instead (e.g., for big-endian CPUs).
*
*/
#ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */
#include <sys/isa_defs.h> /* get endianness selection */
#define PLATFORM_MUST_ALIGN _ALIGNMENT_REQUIRED
#if defined(_BIG_ENDIAN)
/* here for big-endian CPUs */
#define SKEIN_NEED_SWAP (1)
#else
/* here for x86 and x86-64 CPUs (and other detected little-endian CPUs) */
#define SKEIN_NEED_SWAP (0)
#if PLATFORM_MUST_ALIGN == 0 /* ok to use "fast" versions? */
#define Skein_Put64_LSB_First(dst08, src64, bCnt) bcopy(src64, dst08, bCnt)
#define Skein_Get64_LSB_First(dst64, src08, wCnt) \
bcopy(src08, dst64, 8 * (wCnt))
#endif
#endif
#endif /* ifndef SKEIN_NEED_SWAP */
/*
* Provide any definitions still needed.
*/
#ifndef Skein_Swap64 /* swap for big-endian, nop for little-endian */
#if SKEIN_NEED_SWAP
#define Skein_Swap64(w64) \
(((((uint64_t)(w64)) & 0xFF) << 56) | \
(((((uint64_t)(w64)) >> 8) & 0xFF) << 48) | \
(((((uint64_t)(w64)) >> 16) & 0xFF) << 40) | \
(((((uint64_t)(w64)) >> 24) & 0xFF) << 32) | \
(((((uint64_t)(w64)) >> 32) & 0xFF) << 24) | \
(((((uint64_t)(w64)) >> 40) & 0xFF) << 16) | \
(((((uint64_t)(w64)) >> 48) & 0xFF) << 8) | \
(((((uint64_t)(w64)) >> 56) & 0xFF)))
#else
#define Skein_Swap64(w64) (w64)
#endif
#endif /* ifndef Skein_Swap64 */
#ifndef Skein_Put64_LSB_First
void
Skein_Put64_LSB_First(uint8_t *dst, const uint64_t *src, size_t bCnt)
#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */
{
/*
* this version is fully portable (big-endian or little-endian),
* but slow
*/
size_t n;
for (n = 0; n < bCnt; n++)
dst[n] = (uint8_t)(src[n >> 3] >> (8 * (n & 7)));
}
#else
; /* output only the function prototype */
#endif
#endif /* ifndef Skein_Put64_LSB_First */
#ifndef Skein_Get64_LSB_First
void
Skein_Get64_LSB_First(uint64_t *dst, const uint8_t *src, size_t wCnt)
#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */
{
/*
* this version is fully portable (big-endian or little-endian),
* but slow
*/
size_t n;
for (n = 0; n < 8 * wCnt; n += 8)
dst[n / 8] = (((uint64_t)src[n])) +
(((uint64_t)src[n + 1]) << 8) +
(((uint64_t)src[n + 2]) << 16) +
(((uint64_t)src[n + 3]) << 24) +
(((uint64_t)src[n + 4]) << 32) +
(((uint64_t)src[n + 5]) << 40) +
(((uint64_t)src[n + 6]) << 48) +
(((uint64_t)src[n + 7]) << 56);
}
#else
; /* output only the function prototype */
#endif
#endif /* ifndef Skein_Get64_LSB_First */
#endif /* _SKEIN_PORT_H_ */

View File

@ -231,4 +231,17 @@ zpool_feature_init(void)
"org.open-zfs:large_blocks", "large_blocks",
"Support for blocks larger than 128KB.",
ZFEATURE_FLAG_PER_DATASET, large_blocks_deps);
zfeature_register(SPA_FEATURE_SHA512,
"org.illumos:sha512", "sha512",
"SHA-512/256 hash algorithm.",
ZFEATURE_FLAG_PER_DATASET, NULL);
zfeature_register(SPA_FEATURE_SKEIN,
"org.illumos:skein", "skein",
"Skein hash algorithm.",
ZFEATURE_FLAG_PER_DATASET, NULL);
zfeature_register(SPA_FEATURE_EDONR,
"org.illumos:edonr", "edonr",
"Edon-R hash algorithm.",
ZFEATURE_FLAG_PER_DATASET, NULL);
}

View File

@ -52,6 +52,9 @@ typedef enum spa_feature {
SPA_FEATURE_BOOKMARKS,
SPA_FEATURE_FS_SS_LIMIT,
SPA_FEATURE_LARGE_BLOCKS,
SPA_FEATURE_SHA512,
SPA_FEATURE_SKEIN,
SPA_FEATURE_EDONR,
SPA_FEATURES
} spa_feature_t;

View File

@ -22,6 +22,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
/*
* Fletcher Checksums
@ -131,8 +134,10 @@
#include <sys/zio.h>
#include <sys/spa.h>
/*ARGSUSED*/
void
fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
fletcher_2_native(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
const uint64_t *ip = buf;
const uint64_t *ipend = ip + (size / sizeof (uint64_t));
@ -148,8 +153,10 @@ fletcher_2_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1);
}
/*ARGSUSED*/
void
fletcher_2_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp)
fletcher_2_byteswap(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
const uint64_t *ip = buf;
const uint64_t *ipend = ip + (size / sizeof (uint64_t));
@ -165,8 +172,10 @@ fletcher_2_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp)
ZIO_SET_CHECKSUM(zcp, a0, a1, b0, b1);
}
/*ARGSUSED*/
void
fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
fletcher_4_native(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
const uint32_t *ip = buf;
const uint32_t *ipend = ip + (size / sizeof (uint32_t));
@ -182,8 +191,10 @@ fletcher_4_native(const void *buf, uint64_t size, zio_cksum_t *zcp)
ZIO_SET_CHECKSUM(zcp, a, b, c, d);
}
/*ARGSUSED*/
void
fletcher_4_byteswap(const void *buf, uint64_t size, zio_cksum_t *zcp)
fletcher_4_byteswap(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
const uint32_t *ip = buf;
const uint32_t *ipend = ip + (size / sizeof (uint32_t));

View File

@ -22,6 +22,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#ifndef _ZFS_FLETCHER_H
#define _ZFS_FLETCHER_H
@ -37,14 +40,12 @@ extern "C" {
* fletcher checksum functions
*/
void fletcher_2_native(const void *, uint64_t, zio_cksum_t *);
void fletcher_2_byteswap(const void *, uint64_t, zio_cksum_t *);
void fletcher_4_native(const void *, uint64_t, zio_cksum_t *);
void fletcher_4_byteswap(const void *, uint64_t, zio_cksum_t *);
void fletcher_4_incremental_native(const void *, uint64_t,
zio_cksum_t *);
void fletcher_4_incremental_byteswap(const void *, uint64_t,
zio_cksum_t *);
void fletcher_2_native(const void *, uint64_t, const void *, zio_cksum_t *);
void fletcher_2_byteswap(const void *, uint64_t, const void *, zio_cksum_t *);
void fletcher_4_native(const void *, uint64_t, const void *, zio_cksum_t *);
void fletcher_4_byteswap(const void *, uint64_t, const void *, zio_cksum_t *);
void fletcher_4_incremental_native(const void *, uint64_t, zio_cksum_t *);
void fletcher_4_incremental_byteswap(const void *, uint64_t, zio_cksum_t *);
#ifdef __cplusplus
}

View File

@ -71,6 +71,9 @@ zfs_prop_init(void)
{ "fletcher4", ZIO_CHECKSUM_FLETCHER_4 },
{ "sha256", ZIO_CHECKSUM_SHA256 },
{ "noparity", ZIO_CHECKSUM_NOPARITY },
{ "sha512", ZIO_CHECKSUM_SHA512 },
{ "skein", ZIO_CHECKSUM_SKEIN },
{ "edonr", ZIO_CHECKSUM_EDONR },
{ NULL }
};
@ -81,6 +84,14 @@ zfs_prop_init(void)
{ "sha256", ZIO_CHECKSUM_SHA256 },
{ "sha256,verify",
ZIO_CHECKSUM_SHA256 | ZIO_CHECKSUM_VERIFY },
{ "sha512", ZIO_CHECKSUM_SHA512 },
{ "sha512,verify",
ZIO_CHECKSUM_SHA512 | ZIO_CHECKSUM_VERIFY },
{ "skein", ZIO_CHECKSUM_SKEIN },
{ "skein,verify",
ZIO_CHECKSUM_SKEIN | ZIO_CHECKSUM_VERIFY },
{ "edonr,verify",
ZIO_CHECKSUM_EDONR | ZIO_CHECKSUM_VERIFY },
{ NULL }
};
@ -217,12 +228,12 @@ zfs_prop_init(void)
zprop_register_index(ZFS_PROP_CHECKSUM, "checksum",
ZIO_CHECKSUM_DEFAULT, PROP_INHERIT, ZFS_TYPE_FILESYSTEM |
ZFS_TYPE_VOLUME,
"on | off | fletcher2 | fletcher4 | sha256", "CHECKSUM",
checksum_table);
"on | off | fletcher2 | fletcher4 | sha256 | sha512 | "
"skein | edonr", "CHECKSUM", checksum_table);
zprop_register_index(ZFS_PROP_DEDUP, "dedup", ZIO_CHECKSUM_OFF,
PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"on | off | verify | sha256[,verify]", "DEDUP",
dedup_table);
"on | off | verify | sha256[,verify], sha512[,verify], "
"skein[,verify], edonr,verify", "DEDUP", dedup_table);
zprop_register_index(ZFS_PROP_COMPRESSION, "compression",
ZIO_COMPRESS_DEFAULT, PROP_INHERIT,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,

View File

@ -1458,6 +1458,12 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
"property setting is not allowed on "
"bootable datasets"));
(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
} else if (prop == ZFS_PROP_CHECKSUM ||
prop == ZFS_PROP_DEDUP) {
(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property setting is not allowed on "
"root pools"));
(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
} else {
(void) zfs_standard_error(hdl, err, errbuf);
}

View File

@ -1,5 +1,5 @@
'\" te
.\" Copyright (c) 2013 by Delphix. All rights reserved.
.\" Copyright (c) 2012, 2015 by Delphix. All rights reserved.
.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
.\" Copyright (c) 2014, Joyent, Inc. All rights reserved.
.\" The contents of this file are subject to the terms of the Common Development
@ -444,5 +444,114 @@ set larger than 128KB, and will return to being \fBenabled\fR once all
filesystems that have ever had their recordsize larger than 128KB are destroyed.
.RE
.sp
.ne 2
.na
\fB\fBsha512\fR\fR
.ad
.RS 4n
.TS
l l .
GUID org.illumos:sha512
READ\-ONLY COMPATIBLE no
DEPENDENCIES none
.TE
This feature enables the use of the SHA-512/256 truncated hash algorithm
(FIPS 180-4) for checksum and dedup. The native 64-bit arithmetic of
SHA-512 provides an approximate 50% performance boost over SHA-256 on
64-bit hardware and is thus a good minimum-change replacement candidate
for systems where hash performance is important, but these systems
cannot for whatever reason utilize the faster \fBskein\fR and
\fBedonr\fR algorithms.
When the \fBsha512\fR feature is set to \fBenabled\fR, the administrator
can turn on the \fBsha512\fR checksum on any dataset using the
\fBzfs set checksum=sha512\fR(1M) command. This feature becomes
\fBactive\fR once a \fBchecksum\fR property has been set to \fBsha512\fR,
and will return to being \fBenabled\fR once all filesystems that have
ever had their checksum set to \fBsha512\fR are destroyed.
Booting off of pools utilizing SHA-512/256 is supported (provided that
the updated GRUB stage2 module is installed).
.RE
.sp
.ne 2
.na
\fB\fBskein\fR\fR
.ad
.RS 4n
.TS
l l .
GUID org.illumos:skein
READ\-ONLY COMPATIBLE no
DEPENDENCIES none
.TE
This feature enables the use of the Skein hash algorithm for checksum
and dedup. Skein is a high-performance secure hash algorithm that was a
finalist in the NIST SHA-3 competition. It provides a very high security
margin and high performance on 64-bit hardware (80% faster than
SHA-256). This implementation also utilizes the new salted checksumming
functionality in ZFS, which means that the checksum is pre-seeded with a
secret 256-bit random key (stored on the pool) before being fed the data
block to be checksummed. Thus the produced checksums are unique to a
given pool, preventing hash collision attacks on systems with dedup.
When the \fBskein\fR feature is set to \fBenabled\fR, the administrator
can turn on the \fBskein\fR checksum on any dataset using the
\fBzfs set checksum=skein\fR(1M) command. This feature becomes
\fBactive\fR once a \fBchecksum\fR property has been set to \fBskein\fR,
and will return to being \fBenabled\fR once all filesystems that have
ever had their checksum set to \fBskein\fR are destroyed.
Booting off of pools using \fBskein\fR is \fBNOT\fR supported
-- any attempt to enable \fBskein\fR on a root pool will fail with an
error.
.RE
.sp
.ne 2
.na
\fB\fBedonr\fR\fR
.ad
.RS 4n
.TS
l l .
GUID org.illumos:edonr
READ\-ONLY COMPATIBLE no
DEPENDENCIES none
.TE
This feature enables the use of the Edon-R hash algorithm for checksum,
including for nopwrite (if compression is also enabled, an overwrite of
a block whose checksum matches the data being written will be ignored).
In an abundance of caution, Edon-R can not be used with dedup
(without verification).
Edon-R is a very high-performance hash algorithm that was part
of the NIST SHA-3 competition. It provides extremely high hash
performance (over 350% faster than SHA-256), but was not selected
because of its unsuitability as a general purpose secure hash algorithm.
This implementation utilizes the new salted checksumming functionality
in ZFS, which means that the checksum is pre-seeded with a secret
256-bit random key (stored on the pool) before being fed the data block
to be checksummed. Thus the produced checksums are unique to a given
pool.
When the \fBedonr\fR feature is set to \fBenabled\fR, the administrator
can turn on the \fBedonr\fR checksum on any dataset using the
\fBzfs set checksum=edonr\fR(1M) command. This feature becomes
\fBactive\fR once a \fBchecksum\fR property has been set to \fBedonr\fR,
and will return to being \fBenabled\fR once all filesystems that have
ever had their checksum set to \fBedonr\fR are destroyed.
Booting off of pools using \fBedonr\fR is \fBNOT\fR supported
-- any attempt to enable \fBedonr\fR on a root pool will fail with an
error.
.SH "SEE ALSO"
\fBzpool\fR(1M)

View File

@ -509,6 +509,10 @@ SHA1_OBJS += sha1.o sha1_mod.o
SHA2_OBJS += sha2.o sha2_mod.o
SKEIN_OBJS += skein.o skein_block.o skein_iv.o skein_mod.o
EDONR_OBJS += edonr.o edonr_mod.o
IPGPC_OBJS += classifierddi.o classifier.o filters.o trie.o table.o \
ba_table.o
@ -1370,6 +1374,8 @@ ZFS_COMMON_OBJS += \
rrwlock.o \
sa.o \
sha256.o \
edonr_zfs.o \
skein_zfs.o \
spa.o \
spa_config.o \
spa_errlog.o \

View File

@ -0,0 +1,63 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#include <sys/modctl.h>
#include <sys/crypto/common.h>
#include <sys/crypto/spi.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/edonr.h>
/*
* Unlike sha2 or skein, we won't expose edonr via the Kernel Cryptographic
* Framework (KCF), because Edon-R is *NOT* suitable for general-purpose
* cryptographic use. Users of Edon-R must interface directly to this module.
*/
static struct modlmisc modlmisc = {
&mod_miscops,
"Edon-R Message-Digest Algorithm"
};
static struct modlinkage modlinkage = {
MODREV_1, &modlmisc, NULL
};
int
_init(void)
{
int error;
if ((error = mod_install(&modlinkage)) != 0)
return (error);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}

View File

@ -0,0 +1,830 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#include <sys/modctl.h>
#include <sys/crypto/common.h>
#include <sys/crypto/spi.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#define SKEIN_MODULE_IMPL
#include <sys/skein.h>
/*
* Like the sha2 module, we create the skein module with two modlinkages:
* - modlmisc to allow direct calls to Skein_* API functions.
* - modlcrypto to integrate well into the Kernel Crypto Framework (KCF).
*/
static struct modlmisc modlmisc = {
&mod_miscops,
"Skein Message-Digest Algorithm"
};
static struct modlcrypto modlcrypto = {
&mod_cryptoops,
"Skein Kernel SW Provider"
};
static struct modlinkage modlinkage = {
MODREV_1, &modlmisc, &modlcrypto, NULL
};
static crypto_mech_info_t skein_mech_info_tab[] = {
{CKM_SKEIN_256, SKEIN_256_MECH_INFO_TYPE,
CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
{CKM_SKEIN_256_MAC, SKEIN_256_MAC_MECH_INFO_TYPE,
CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
CRYPTO_KEYSIZE_UNIT_IN_BYTES},
{CKM_SKEIN_512, SKEIN_512_MECH_INFO_TYPE,
CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
{CKM_SKEIN_512_MAC, SKEIN_512_MAC_MECH_INFO_TYPE,
CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
CRYPTO_KEYSIZE_UNIT_IN_BYTES},
{CKM_SKEIN1024, SKEIN1024_MECH_INFO_TYPE,
CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
{CKM_SKEIN1024_MAC, SKEIN1024_MAC_MECH_INFO_TYPE,
CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
CRYPTO_KEYSIZE_UNIT_IN_BYTES}
};
static void skein_provider_status(crypto_provider_handle_t, uint_t *);
static crypto_control_ops_t skein_control_ops = {
skein_provider_status
};
static int skein_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
crypto_req_handle_t);
static int skein_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
crypto_req_handle_t);
static int skein_update(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
static int skein_final(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
static int skein_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
crypto_req_handle_t);
static crypto_digest_ops_t skein_digest_ops = {
skein_digest_init,
skein_digest,
skein_update,
NULL,
skein_final,
skein_digest_atomic
};
static int skein_mac_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
crypto_spi_ctx_template_t, crypto_req_handle_t);
static int skein_mac_atomic(crypto_provider_handle_t, crypto_session_id_t,
crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
crypto_spi_ctx_template_t, crypto_req_handle_t);
static crypto_mac_ops_t skein_mac_ops = {
skein_mac_init,
NULL,
skein_update, /* using regular digest update is OK here */
skein_final, /* using regular digest final is OK here */
skein_mac_atomic,
NULL
};
static int skein_create_ctx_template(crypto_provider_handle_t,
crypto_mechanism_t *, crypto_key_t *, crypto_spi_ctx_template_t *,
size_t *, crypto_req_handle_t);
static int skein_free_context(crypto_ctx_t *);
static crypto_ctx_ops_t skein_ctx_ops = {
skein_create_ctx_template,
skein_free_context
};
static crypto_ops_t skein_crypto_ops = {
&skein_control_ops,
&skein_digest_ops,
NULL,
&skein_mac_ops,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&skein_ctx_ops,
NULL,
NULL,
NULL
};
static crypto_provider_info_t skein_prov_info = {
CRYPTO_SPI_VERSION_4,
"Skein Software Provider",
CRYPTO_SW_PROVIDER,
{&modlinkage},
NULL,
&skein_crypto_ops,
sizeof (skein_mech_info_tab) / sizeof (crypto_mech_info_t),
skein_mech_info_tab
};
static crypto_kcf_provider_handle_t skein_prov_handle = NULL;
typedef struct skein_ctx {
skein_mech_type_t sc_mech_type;
size_t sc_digest_bitlen;
/*LINTED(E_ANONYMOUS_UNION_DECL)*/
union {
Skein_256_Ctxt_t sc_256;
Skein_512_Ctxt_t sc_512;
Skein1024_Ctxt_t sc_1024;
};
} skein_ctx_t;
#define SKEIN_CTX(_ctx_) ((skein_ctx_t *)((_ctx_)->cc_provider_private))
#define SKEIN_CTX_LVALUE(_ctx_) (_ctx_)->cc_provider_private
#define SKEIN_OP(_skein_ctx, _op, ...) \
do { \
skein_ctx_t *sc = (_skein_ctx); \
switch (sc->sc_mech_type) { \
case SKEIN_256_MECH_INFO_TYPE: \
case SKEIN_256_MAC_MECH_INFO_TYPE: \
(void) Skein_256_ ## _op(&sc->sc_256, __VA_ARGS__);\
break; \
case SKEIN_512_MECH_INFO_TYPE: \
case SKEIN_512_MAC_MECH_INFO_TYPE: \
(void) Skein_512_ ## _op(&sc->sc_512, __VA_ARGS__);\
break; \
case SKEIN1024_MECH_INFO_TYPE: \
case SKEIN1024_MAC_MECH_INFO_TYPE: \
(void) Skein1024_ ## _op(&sc->sc_1024, __VA_ARGS__);\
break; \
} \
_NOTE(CONSTCOND) \
} while (0)
static int
skein_get_digest_bitlen(const crypto_mechanism_t *mechanism, size_t *result)
{
if (mechanism->cm_param != NULL) {
/*LINTED(E_BAD_PTR_CAST_ALIGN)*/
skein_param_t *param = (skein_param_t *)mechanism->cm_param;
if (mechanism->cm_param_len != sizeof (*param) ||
param->sp_digest_bitlen == 0) {
return (CRYPTO_MECHANISM_PARAM_INVALID);
}
*result = param->sp_digest_bitlen;
} else {
switch (mechanism->cm_type) {
case SKEIN_256_MECH_INFO_TYPE:
*result = 256;
break;
case SKEIN_512_MECH_INFO_TYPE:
*result = 512;
break;
case SKEIN1024_MECH_INFO_TYPE:
*result = 1024;
break;
default:
return (CRYPTO_MECHANISM_INVALID);
}
}
return (CRYPTO_SUCCESS);
}
int
_init(void)
{
int error;
if ((error = mod_install(&modlinkage)) != 0)
return (error);
/*
* Try to register with KCF - failure shouldn't unload us, since we
* still may want to continue providing misc/skein functionality.
*/
(void) crypto_register_provider(&skein_prov_info, &skein_prov_handle);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/*
* KCF software provider control entry points.
*/
/* ARGSUSED */
static void
skein_provider_status(crypto_provider_handle_t provider, uint_t *status)
{
*status = CRYPTO_PROVIDER_READY;
}
/*
* General Skein hashing helper functions.
*/
/*
* Performs an Update on a context with uio input data.
*/
static int
skein_digest_update_uio(skein_ctx_t *ctx, const crypto_data_t *data)
{
off_t offset = data->cd_offset;
size_t length = data->cd_length;
uint_t vec_idx;
size_t cur_len;
const uio_t *uio = data->cd_uio;
/* we support only kernel buffer */
if (uio->uio_segflg != UIO_SYSSPACE)
return (CRYPTO_ARGUMENTS_BAD);
/*
* Jump to the first iovec containing data to be
* digested.
*/
for (vec_idx = 0; vec_idx < uio->uio_iovcnt &&
offset >= uio->uio_iov[vec_idx].iov_len;
offset -= uio->uio_iov[vec_idx++].iov_len)
;
if (vec_idx == uio->uio_iovcnt) {
/*
* The caller specified an offset that is larger than the
* total size of the buffers it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
/*
* Now do the digesting on the iovecs.
*/
while (vec_idx < uio->uio_iovcnt && length > 0) {
cur_len = MIN(uio->uio_iov[vec_idx].iov_len - offset, length);
SKEIN_OP(ctx, Update, (uint8_t *)uio->uio_iov[vec_idx].iov_base
+ offset, cur_len);
length -= cur_len;
vec_idx++;
offset = 0;
}
if (vec_idx == uio->uio_iovcnt && length > 0) {
/*
* The end of the specified iovec's was reached but
* the length requested could not be processed, i.e.
* The caller requested to digest more data than it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
return (CRYPTO_SUCCESS);
}
/*
* Performs a Final on a context and writes to a uio digest output.
*/
static int
skein_digest_final_uio(skein_ctx_t *ctx, crypto_data_t *digest,
crypto_req_handle_t req)
{
off_t offset = digest->cd_offset;
uint_t vec_idx;
uio_t *uio = digest->cd_uio;
/* we support only kernel buffer */
if (uio->uio_segflg != UIO_SYSSPACE)
return (CRYPTO_ARGUMENTS_BAD);
/*
* Jump to the first iovec containing ptr to the digest to be returned.
*/
for (vec_idx = 0; offset >= uio->uio_iov[vec_idx].iov_len &&
vec_idx < uio->uio_iovcnt;
offset -= uio->uio_iov[vec_idx++].iov_len)
;
if (vec_idx == uio->uio_iovcnt) {
/*
* The caller specified an offset that is larger than the
* total size of the buffers it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
if (offset + CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen) <=
uio->uio_iov[vec_idx].iov_len) {
/* The computed digest will fit in the current iovec. */
SKEIN_OP(ctx, Final,
(uchar_t *)uio->uio_iov[vec_idx].iov_base + offset);
} else {
uint8_t *digest_tmp;
off_t scratch_offset = 0;
size_t length = CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen);
size_t cur_len;
digest_tmp = kmem_alloc(CRYPTO_BITS2BYTES(
ctx->sc_digest_bitlen), crypto_kmflag(req));
if (digest_tmp == NULL)
return (CRYPTO_HOST_MEMORY);
SKEIN_OP(ctx, Final, digest_tmp);
while (vec_idx < uio->uio_iovcnt && length > 0) {
cur_len = MIN(uio->uio_iov[vec_idx].iov_len - offset,
length);
bcopy(digest_tmp + scratch_offset,
uio->uio_iov[vec_idx].iov_base + offset, cur_len);
length -= cur_len;
vec_idx++;
scratch_offset += cur_len;
offset = 0;
}
kmem_free(digest_tmp, CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen));
if (vec_idx == uio->uio_iovcnt && length > 0) {
/*
* The end of the specified iovec's was reached but
* the length requested could not be processed, i.e.
* The caller requested to digest more data than it
* provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
}
return (CRYPTO_SUCCESS);
}
/*
* Performs an Update on a context with mblk input data.
*/
static int
skein_digest_update_mblk(skein_ctx_t *ctx, crypto_data_t *data)
{
off_t offset = data->cd_offset;
size_t length = data->cd_length;
mblk_t *mp;
size_t cur_len;
/* Jump to the first mblk_t containing data to be digested. */
for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
offset -= MBLKL(mp), mp = mp->b_cont)
;
if (mp == NULL) {
/*
* The caller specified an offset that is larger than the
* total size of the buffers it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
/* Now do the digesting on the mblk chain. */
while (mp != NULL && length > 0) {
cur_len = MIN(MBLKL(mp) - offset, length);
SKEIN_OP(ctx, Update, mp->b_rptr + offset, cur_len);
length -= cur_len;
offset = 0;
mp = mp->b_cont;
}
if (mp == NULL && length > 0) {
/*
* The end of the mblk was reached but the length requested
* could not be processed, i.e. The caller requested
* to digest more data than it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
return (CRYPTO_SUCCESS);
}
/*
* Performs a Final on a context and writes to an mblk digest output.
*/
static int
skein_digest_final_mblk(skein_ctx_t *ctx, crypto_data_t *digest,
crypto_req_handle_t req)
{
off_t offset = digest->cd_offset;
mblk_t *mp;
/* Jump to the first mblk_t that will be used to store the digest. */
for (mp = digest->cd_mp; mp != NULL && offset >= MBLKL(mp);
offset -= MBLKL(mp), mp = mp->b_cont)
;
if (mp == NULL) {
/* caller specified offset is too large */
return (CRYPTO_DATA_LEN_RANGE);
}
if (offset + CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen) <= MBLKL(mp)) {
/* The digest will fit in the current mblk. */
SKEIN_OP(ctx, Final, mp->b_rptr + offset);
} else {
/* Split the digest up between the individual buffers. */
uint8_t *digest_tmp;
off_t scratch_offset = 0;
size_t length = CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen);
size_t cur_len;
digest_tmp = kmem_alloc(CRYPTO_BITS2BYTES(
ctx->sc_digest_bitlen), crypto_kmflag(req));
if (digest_tmp == NULL)
return (CRYPTO_HOST_MEMORY);
SKEIN_OP(ctx, Final, digest_tmp);
while (mp != NULL && length > 0) {
cur_len = MIN(MBLKL(mp) - offset, length);
bcopy(digest_tmp + scratch_offset,
mp->b_rptr + offset, cur_len);
length -= cur_len;
mp = mp->b_cont;
scratch_offset += cur_len;
offset = 0;
}
kmem_free(digest_tmp, CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen));
if (mp == NULL && length > 0) {
/* digest too long to fit in the mblk buffers */
return (CRYPTO_DATA_LEN_RANGE);
}
}
return (CRYPTO_SUCCESS);
}
/*
* KCF software provider digest entry points.
*/
/*
* Initializes a skein digest context to the configuration in `mechanism'.
* The mechanism cm_type must be one of SKEIN_*_MECH_INFO_TYPE. The cm_param
* field may contain a skein_param_t structure indicating the length of the
* digest the algorithm should produce. Otherwise the default output lengths
* are applied (32 bytes for Skein-256, 64 bytes for Skein-512 and 128 bytes
* for Skein-1024).
*/
static int
skein_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_req_handle_t req)
{
int error = CRYPTO_SUCCESS;
if (!VALID_SKEIN_DIGEST_MECH(mechanism->cm_type))
return (CRYPTO_MECHANISM_INVALID);
SKEIN_CTX_LVALUE(ctx) = kmem_alloc(sizeof (*SKEIN_CTX(ctx)),
crypto_kmflag(req));
if (SKEIN_CTX(ctx) == NULL)
return (CRYPTO_HOST_MEMORY);
SKEIN_CTX(ctx)->sc_mech_type = mechanism->cm_type;
error = skein_get_digest_bitlen(mechanism,
&SKEIN_CTX(ctx)->sc_digest_bitlen);
if (error != CRYPTO_SUCCESS)
goto errout;
SKEIN_OP(SKEIN_CTX(ctx), Init, SKEIN_CTX(ctx)->sc_digest_bitlen);
return (CRYPTO_SUCCESS);
errout:
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
SKEIN_CTX_LVALUE(ctx) = NULL;
return (error);
}
/*
* Executes a skein_update and skein_digest on a pre-initialized crypto
* context in a single step. See the documentation to these functions to
* see what to pass here.
*/
static int
skein_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
crypto_req_handle_t req)
{
int error = CRYPTO_SUCCESS;
ASSERT(SKEIN_CTX(ctx) != NULL);
if (digest->cd_length <
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen)) {
digest->cd_length =
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
return (CRYPTO_BUFFER_TOO_SMALL);
}
error = skein_update(ctx, data, req);
if (error != CRYPTO_SUCCESS) {
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
SKEIN_CTX_LVALUE(ctx) = NULL;
digest->cd_length = 0;
return (error);
}
error = skein_final(ctx, digest, req);
return (error);
}
/*
* Performs a skein Update with the input message in `data' (successive calls
* can push more data). This is used both for digest and MAC operation.
* Supported input data formats are raw, uio and mblk.
*/
/*ARGSUSED*/
static int
skein_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req)
{
int error = CRYPTO_SUCCESS;
ASSERT(SKEIN_CTX(ctx) != NULL);
switch (data->cd_format) {
case CRYPTO_DATA_RAW:
SKEIN_OP(SKEIN_CTX(ctx), Update,
(uint8_t *)data->cd_raw.iov_base + data->cd_offset,
data->cd_length);
break;
case CRYPTO_DATA_UIO:
error = skein_digest_update_uio(SKEIN_CTX(ctx), data);
break;
case CRYPTO_DATA_MBLK:
error = skein_digest_update_mblk(SKEIN_CTX(ctx), data);
break;
default:
error = CRYPTO_ARGUMENTS_BAD;
}
return (error);
}
/*
* Performs a skein Final, writing the output to `digest'. This is used both
* for digest and MAC operation.
* Supported output digest formats are raw, uio and mblk.
*/
/*ARGSUSED*/
static int
skein_final(crypto_ctx_t *ctx, crypto_data_t *digest, crypto_req_handle_t req)
{
int error = CRYPTO_SUCCESS;
ASSERT(SKEIN_CTX(ctx) != NULL);
if (digest->cd_length <
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen)) {
digest->cd_length =
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
return (CRYPTO_BUFFER_TOO_SMALL);
}
switch (digest->cd_format) {
case CRYPTO_DATA_RAW:
SKEIN_OP(SKEIN_CTX(ctx), Final,
(uint8_t *)digest->cd_raw.iov_base + digest->cd_offset);
break;
case CRYPTO_DATA_UIO:
error = skein_digest_final_uio(SKEIN_CTX(ctx), digest, req);
break;
case CRYPTO_DATA_MBLK:
error = skein_digest_final_mblk(SKEIN_CTX(ctx), digest, req);
break;
default:
error = CRYPTO_ARGUMENTS_BAD;
}
if (error == CRYPTO_SUCCESS)
digest->cd_length =
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
else
digest->cd_length = 0;
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
kmem_free(SKEIN_CTX(ctx), sizeof (*(SKEIN_CTX(ctx))));
SKEIN_CTX_LVALUE(ctx) = NULL;
return (error);
}
/*
* Performs a full skein digest computation in a single call, configuring the
* algorithm according to `mechanism', reading the input to be digested from
* `data' and writing the output to `digest'.
* Supported input/output formats are raw, uio and mblk.
*/
/*ARGSUSED*/
static int
skein_digest_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
crypto_data_t *data, crypto_data_t *digest, crypto_req_handle_t req)
{
int error;
skein_ctx_t skein_ctx;
crypto_ctx_t ctx;
SKEIN_CTX_LVALUE(&ctx) = &skein_ctx;
/* Init */
if (!VALID_SKEIN_DIGEST_MECH(mechanism->cm_type))
return (CRYPTO_MECHANISM_INVALID);
skein_ctx.sc_mech_type = mechanism->cm_type;
error = skein_get_digest_bitlen(mechanism, &skein_ctx.sc_digest_bitlen);
if (error != CRYPTO_SUCCESS)
goto out;
SKEIN_OP(&skein_ctx, Init, skein_ctx.sc_digest_bitlen);
if ((error = skein_update(&ctx, data, digest)) != CRYPTO_SUCCESS)
goto out;
if ((error = skein_final(&ctx, data, digest)) != CRYPTO_SUCCESS)
goto out;
out:
if (error == CRYPTO_SUCCESS)
digest->cd_length =
CRYPTO_BITS2BYTES(skein_ctx.sc_digest_bitlen);
else
digest->cd_length = 0;
bzero(&skein_ctx, sizeof (skein_ctx));
return (error);
}
/*
* Helper function that builds a Skein MAC context from the provided
* mechanism and key.
*/
static int
skein_mac_ctx_build(skein_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_key_t *key)
{
int error;
if (!VALID_SKEIN_MAC_MECH(mechanism->cm_type))
return (CRYPTO_MECHANISM_INVALID);
if (key->ck_format != CRYPTO_KEY_RAW)
return (CRYPTO_ARGUMENTS_BAD);
ctx->sc_mech_type = mechanism->cm_type;
error = skein_get_digest_bitlen(mechanism, &ctx->sc_digest_bitlen);
if (error != CRYPTO_SUCCESS)
return (error);
SKEIN_OP(ctx, InitExt, ctx->sc_digest_bitlen, 0, key->ck_data,
CRYPTO_BITS2BYTES(key->ck_length));
return (CRYPTO_SUCCESS);
}
/*
* KCF software provide mac entry points.
*/
/*
* Initializes a skein MAC context. You may pass a ctx_template, in which
* case the template will be reused to make initialization more efficient.
* Otherwise a new context will be constructed. The mechanism cm_type must
* be one of SKEIN_*_MAC_MECH_INFO_TYPE. Same as in skein_digest_init, you
* may pass a skein_param_t in cm_param to configure the length of the
* digest. The key must be in raw format.
*/
static int
skein_mac_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
crypto_req_handle_t req)
{
int error;
SKEIN_CTX_LVALUE(ctx) = kmem_alloc(sizeof (*SKEIN_CTX(ctx)),
crypto_kmflag(req));
if (SKEIN_CTX(ctx) == NULL)
return (CRYPTO_HOST_MEMORY);
if (ctx_template != NULL) {
bcopy(ctx_template, SKEIN_CTX(ctx),
sizeof (*SKEIN_CTX(ctx)));
} else {
error = skein_mac_ctx_build(SKEIN_CTX(ctx), mechanism, key);
if (error != CRYPTO_SUCCESS)
goto errout;
}
return (CRYPTO_SUCCESS);
errout:
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
return (error);
}
/*
* The MAC update and final calls are reused from the regular digest code.
*/
/*ARGSUSED*/
/*
* Same as skein_digest_atomic, performs an atomic Skein MAC operation in
* one step. All the same properties apply to the arguments of this
* function as to those of the partial operations above.
*/
static int
skein_mac_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *data, crypto_data_t *mac,
crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
/* faux crypto context just for skein_digest_{update,final} */
int error;
crypto_ctx_t ctx;
skein_ctx_t skein_ctx;
SKEIN_CTX_LVALUE(&ctx) = &skein_ctx;
if (ctx_template != NULL) {
bcopy(ctx_template, &skein_ctx, sizeof (skein_ctx));
} else {
error = skein_mac_ctx_build(&skein_ctx, mechanism, key);
if (error != CRYPTO_SUCCESS)
goto errout;
}
if ((error = skein_update(&ctx, data, req)) != CRYPTO_SUCCESS)
goto errout;
if ((error = skein_final(&ctx, mac, req)) != CRYPTO_SUCCESS)
goto errout;
return (CRYPTO_SUCCESS);
errout:
bzero(&skein_ctx, sizeof (skein_ctx));
return (error);
}
/*
* KCF software provider context management entry points.
*/
/*
* Constructs a context template for the Skein MAC algorithm. The same
* properties apply to the arguments of this function as to those of
* skein_mac_init.
*/
/*ARGSUSED*/
static int
skein_create_ctx_template(crypto_provider_handle_t provider,
crypto_mechanism_t *mechanism, crypto_key_t *key,
crypto_spi_ctx_template_t *ctx_template, size_t *ctx_template_size,
crypto_req_handle_t req)
{
int error;
skein_ctx_t *ctx_tmpl;
ctx_tmpl = kmem_alloc(sizeof (*ctx_tmpl), crypto_kmflag(req));
if (ctx_tmpl == NULL)
return (CRYPTO_HOST_MEMORY);
error = skein_mac_ctx_build(ctx_tmpl, mechanism, key);
if (error != CRYPTO_SUCCESS)
goto errout;
*ctx_template = ctx_tmpl;
*ctx_template_size = sizeof (*ctx_tmpl);
return (CRYPTO_SUCCESS);
errout:
bzero(ctx_tmpl, sizeof (*ctx_tmpl));
kmem_free(ctx_tmpl, sizeof (*ctx_tmpl));
return (error);
}
/*
* Frees a skein context in a parent crypto context.
*/
static int
skein_free_context(crypto_ctx_t *ctx)
{
if (SKEIN_CTX(ctx) != NULL) {
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
SKEIN_CTX_LVALUE(ctx) = NULL;
}
return (CRYPTO_SUCCESS);
}

View File

@ -1372,7 +1372,7 @@ arc_cksum_verify(arc_buf_t *buf)
mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock);
return;
}
fletcher_2_native(buf->b_data, buf->b_hdr->b_size, &zc);
fletcher_2_native(buf->b_data, buf->b_hdr->b_size, NULL, &zc);
if (!ZIO_CHECKSUM_EQUAL(*buf->b_hdr->b_freeze_cksum, zc))
panic("buffer modified while frozen!");
mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock);
@ -1385,7 +1385,7 @@ arc_cksum_equal(arc_buf_t *buf)
int equal;
mutex_enter(&buf->b_hdr->b_l1hdr.b_freeze_lock);
fletcher_2_native(buf->b_data, buf->b_hdr->b_size, &zc);
fletcher_2_native(buf->b_data, buf->b_hdr->b_size, NULL, &zc);
equal = ZIO_CHECKSUM_EQUAL(*buf->b_hdr->b_freeze_cksum, zc);
mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock);
@ -1405,7 +1405,7 @@ arc_cksum_compute(arc_buf_t *buf, boolean_t force)
}
buf->b_hdr->b_freeze_cksum = kmem_alloc(sizeof (zio_cksum_t), KM_SLEEP);
fletcher_2_native(buf->b_data, buf->b_hdr->b_size,
buf->b_hdr->b_freeze_cksum);
NULL, buf->b_hdr->b_freeze_cksum);
mutex_exit(&buf->b_hdr->b_l1hdr.b_freeze_lock);
arc_buf_watch(buf);
}

View File

@ -21,7 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
*/
#include <sys/zfs_context.h>
@ -59,7 +59,8 @@ ddt_object_create(ddt_t *ddt, enum ddt_type type, enum ddt_class class,
spa_t *spa = ddt->ddt_spa;
objset_t *os = ddt->ddt_os;
uint64_t *objectp = &ddt->ddt_object[type][class];
boolean_t prehash = zio_checksum_table[ddt->ddt_checksum].ci_dedup;
boolean_t prehash = zio_checksum_table[ddt->ddt_checksum].ci_flags &
ZCHECKSUM_FLAG_DEDUP;
char name[DDT_NAMELEN];
ddt_object_name(ddt, type, class, name);

View File

@ -1424,7 +1424,8 @@ dmu_sync_done(zio_t *zio, arc_buf_t *buf, void *varg)
ASSERT(BP_EQUAL(bp, bp_orig));
ASSERT(zio->io_prop.zp_compress != ZIO_COMPRESS_OFF);
ASSERT(zio_checksum_table[chksum].ci_dedup);
ASSERT(zio_checksum_table[chksum].ci_flags &
ZCHECKSUM_FLAG_NOPWRITE);
}
dr->dt.dl.dr_overridden_by = *zio->io_bp;
dr->dt.dl.dr_override_state = DR_OVERRIDDEN;
@ -1769,8 +1770,10 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp)
* as well. Otherwise, the metadata checksum defaults
* to fletcher4.
*/
if (zio_checksum_table[checksum].ci_correctable < 1 ||
zio_checksum_table[checksum].ci_eck)
if (!(zio_checksum_table[checksum].ci_flags &
ZCHECKSUM_FLAG_METADATA) ||
(zio_checksum_table[checksum].ci_flags &
ZCHECKSUM_FLAG_EMBEDDED))
checksum = ZIO_CHECKSUM_FLETCHER_4;
if (os->os_redundant_metadata == ZFS_REDUNDANT_METADATA_ALL ||
@ -1809,17 +1812,20 @@ dmu_write_policy(objset_t *os, dnode_t *dn, int level, int wp, zio_prop_t *zp)
*/
if (dedup_checksum != ZIO_CHECKSUM_OFF) {
dedup = (wp & WP_DMU_SYNC) ? B_FALSE : B_TRUE;
if (!zio_checksum_table[checksum].ci_dedup)
if (!(zio_checksum_table[checksum].ci_flags &
ZCHECKSUM_FLAG_DEDUP))
dedup_verify = B_TRUE;
}
/*
* Enable nopwrite if we have a cryptographically secure
* checksum that has no known collisions (i.e. SHA-256)
* and compression is enabled. We don't enable nopwrite if
* dedup is enabled as the two features are mutually exclusive.
* Enable nopwrite if we have secure enough checksum
* algorithm (see comment in zio_nop_write) and
* compression is enabled. We don't enable nopwrite if
* dedup is enabled as the two features are mutually
* exclusive.
*/
nopwrite = (!dedup && zio_checksum_table[checksum].ci_dedup &&
nopwrite = (!dedup && (zio_checksum_table[checksum].ci_flags &
ZCHECKSUM_FLAG_NOPWRITE) &&
compress != ZIO_COMPRESS_OFF && zfs_nopwrite_enabled);
}

View File

@ -268,7 +268,8 @@ dump_write(dmu_sendarg_t *dsp, dmu_object_type_t type,
drrw->drr_checksumtype = ZIO_CHECKSUM_OFF;
} else {
drrw->drr_checksumtype = BP_GET_CHECKSUM(bp);
if (zio_checksum_table[drrw->drr_checksumtype].ci_dedup)
if (zio_checksum_table[drrw->drr_checksumtype].ci_flags &
ZCHECKSUM_FLAG_DEDUP)
drrw->drr_checksumflags |= DRR_CHECKSUM_DEDUP;
DDK_SET_LSIZE(&drrw->drr_key, BP_GET_LSIZE(bp));
DDK_SET_PSIZE(&drrw->drr_key, BP_GET_PSIZE(bp));

View File

@ -50,6 +50,8 @@
#include <sys/dsl_destroy.h>
#include <sys/dsl_userhold.h>
#include <sys/dsl_bookmark.h>
#include <sys/dmu_send.h>
#include <sys/zio_checksum.h>
/*
* The SPA supports block sizes up to 16MB. However, very large blocks
@ -124,10 +126,16 @@ dsl_dataset_block_born(dsl_dataset_t *ds, const blkptr_t *bp, dmu_tx_t *tx)
dsl_dataset_phys(ds)->ds_compressed_bytes += compressed;
dsl_dataset_phys(ds)->ds_uncompressed_bytes += uncompressed;
dsl_dataset_phys(ds)->ds_unique_bytes += used;
if (BP_GET_LSIZE(bp) > SPA_OLD_MAXBLOCKSIZE) {
ds->ds_feature_activation_needed[SPA_FEATURE_LARGE_BLOCKS] =
B_TRUE;
}
spa_feature_t f = zio_checksum_to_feature(BP_GET_CHECKSUM(bp));
if (f != SPA_FEATURE_NONE)
ds->ds_feature_activation_needed[f] = B_TRUE;
mutex_exit(&ds->ds_lock);
dsl_dir_diduse_space(ds->ds_dir, DD_USED_HEAD, delta,
compressed, uncompressed, tx);

View File

@ -0,0 +1,102 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/zfs_context.h>
#include <sys/zio.h>
#include <sys/edonr.h>
#define EDONR_MODE 512
#define EDONR_BLOCK_SIZE EdonR512_BLOCK_SIZE
/*
* Native zio_checksum interface for the Edon-R hash function.
*/
/*ARGSUSED*/
void
zio_checksum_edonr_native(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
uint8_t digest[EDONR_MODE / 8];
EdonRState ctx;
ASSERT(ctx_template != NULL);
bcopy(ctx_template, &ctx, sizeof (ctx));
EdonRUpdate(&ctx, buf, size * 8);
EdonRFinal(&ctx, digest);
bcopy(digest, zcp->zc_word, sizeof (zcp->zc_word));
}
/*
* Byteswapped zio_checksum interface for the Edon-R hash function.
*/
void
zio_checksum_edonr_byteswap(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
zio_cksum_t tmp;
zio_checksum_edonr_native(buf, size, ctx_template, &tmp);
zcp->zc_word[0] = BSWAP_64(zcp->zc_word[0]);
zcp->zc_word[1] = BSWAP_64(zcp->zc_word[1]);
zcp->zc_word[2] = BSWAP_64(zcp->zc_word[2]);
zcp->zc_word[3] = BSWAP_64(zcp->zc_word[3]);
}
void *
zio_checksum_edonr_tmpl_init(const zio_cksum_salt_t *salt)
{
EdonRState *ctx;
uint8_t salt_block[EDONR_BLOCK_SIZE];
/*
* Edon-R needs all but the last hash invocation to be on full-size
* blocks, but the salt is too small. Rather than simply padding it
* with zeros, we expand the salt into a new salt block of proper
* size by double-hashing it (the new salt block will be composed of
* H(salt) || H(H(salt))).
*/
CTASSERT(EDONR_BLOCK_SIZE == 2 * (EDONR_MODE / 8));
EdonRHash(EDONR_MODE, salt->zcs_bytes, sizeof (salt->zcs_bytes) * 8,
salt_block);
EdonRHash(EDONR_MODE, salt_block, EDONR_MODE, salt_block +
EDONR_MODE / 8);
/*
* Feed the new salt block into the hash function - this will serve
* as our MAC key.
*/
ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
EdonRInit(ctx, EDONR_MODE);
EdonRUpdate(ctx, salt_block, sizeof (salt_block) * 8);
return (ctx);
}
void
zio_checksum_edonr_tmpl_free(void *ctx_template)
{
EdonRState *ctx = ctx_template;
bzero(ctx, sizeof (*ctx));
kmem_free(ctx, sizeof (*ctx));
}

View File

@ -22,12 +22,17 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#include <sys/zfs_context.h>
#include <sys/zio.h>
#include <sys/sha2.h>
/*ARGSUSED*/
void
zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp)
zio_checksum_SHA256(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
SHA2_CTX ctx;
zio_cksum_t tmp;
@ -48,3 +53,29 @@ zio_checksum_SHA256(const void *buf, uint64_t size, zio_cksum_t *zcp)
zcp->zc_word[2] = BE_64(tmp.zc_word[2]);
zcp->zc_word[3] = BE_64(tmp.zc_word[3]);
}
/*ARGSUSED*/
void
zio_checksum_SHA512_native(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
SHA2_CTX ctx;
SHA2Init(SHA512_256, &ctx);
SHA2Update(&ctx, buf, size);
SHA2Final(zcp, &ctx);
}
/*ARGSUSED*/
void
zio_checksum_SHA512_byteswap(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
zio_cksum_t tmp;
zio_checksum_SHA512_native(buf, size, ctx_template, &tmp);
zcp->zc_word[0] = BSWAP_64(tmp.zc_word[0]);
zcp->zc_word[1] = BSWAP_64(tmp.zc_word[1]);
zcp->zc_word[2] = BSWAP_64(tmp.zc_word[2]);
zcp->zc_word[3] = BSWAP_64(tmp.zc_word[3]);
}

View File

@ -0,0 +1,91 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#include <sys/zfs_context.h>
#include <sys/zio.h>
#include <sys/skein.h>
/*
* Computes a native 256-bit skein MAC checksum. Please note that this
* function requires the presence of a ctx_template that should be allocated
* using zio_checksum_skein_tmpl_init.
*/
/*ARGSUSED*/
void
zio_checksum_skein_native(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
Skein_512_Ctxt_t ctx;
ASSERT(ctx_template != NULL);
bcopy(ctx_template, &ctx, sizeof (ctx));
(void) Skein_512_Update(&ctx, buf, size);
(void) Skein_512_Final(&ctx, (uint8_t *)zcp);
bzero(&ctx, sizeof (ctx));
}
/*
* Byteswapped version of zio_checksum_skein_native. This just invokes
* the native checksum function and byteswaps the resulting checksum (since
* skein is internally endian-insensitive).
*/
void
zio_checksum_skein_byteswap(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
zio_cksum_t tmp;
zio_checksum_skein_native(buf, size, ctx_template, &tmp);
zcp->zc_word[0] = BSWAP_64(tmp.zc_word[0]);
zcp->zc_word[1] = BSWAP_64(tmp.zc_word[1]);
zcp->zc_word[2] = BSWAP_64(tmp.zc_word[2]);
zcp->zc_word[3] = BSWAP_64(tmp.zc_word[3]);
}
/*
* Allocates a skein MAC template suitable for using in skein MAC checksum
* computations and returns a pointer to it.
*/
void *
zio_checksum_skein_tmpl_init(const zio_cksum_salt_t *salt)
{
Skein_512_Ctxt_t *ctx;
ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP);
(void) Skein_512_InitExt(ctx, sizeof (zio_cksum_t) * 8, 0,
salt->zcs_bytes, sizeof (salt->zcs_bytes));
return (ctx);
}
/*
* Frees a skein context template previously allocated using
* zio_checksum_skein_tmpl_init.
*/
void
zio_checksum_skein_tmpl_free(void *ctx_template)
{
Skein_512_Ctxt_t *ctx = ctx_template;
bzero(ctx, sizeof (*ctx));
kmem_free(ctx, sizeof (*ctx));
}

View File

@ -24,6 +24,7 @@
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
* Copyright (c) 2015, Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
/*
@ -2512,6 +2513,19 @@ spa_load_impl(spa_t *spa, uint64_t pool_guid, nvlist_t *config,
return (spa_load(spa, state, SPA_IMPORT_EXISTING, B_TRUE));
}
/* Grab the secret checksum salt from the MOS. */
error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_CHECKSUM_SALT, 1,
sizeof (spa->spa_cksum_salt.zcs_bytes),
spa->spa_cksum_salt.zcs_bytes);
if (error == ENOENT) {
/* Generate a new salt for subsequent use */
(void) random_get_pseudo_bytes(spa->spa_cksum_salt.zcs_bytes,
sizeof (spa->spa_cksum_salt.zcs_bytes));
} else if (error != 0) {
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));
}
if (spa_dir_prop(spa, DMU_POOL_SYNC_BPOBJ, &obj) != 0)
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, EIO));
error = bpobj_open(&spa->spa_deferred_bpobj, spa->spa_meta_objset, obj);
@ -3664,6 +3678,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
if (version >= SPA_VERSION_ZPOOL_HISTORY)
spa_history_create_obj(spa, tx);
/*
* Generate some random noise for salted checksums to operate on.
*/
(void) random_get_pseudo_bytes(spa->spa_cksum_salt.zcs_bytes,
sizeof (spa->spa_cksum_salt.zcs_bytes));
/*
* Set pool properties.
*/
@ -6215,6 +6235,20 @@ spa_sync_upgrades(spa_t *spa, dmu_tx_t *tx)
if (lz4_en && !lz4_ac)
spa_feature_incr(spa, SPA_FEATURE_LZ4_COMPRESS, tx);
}
/*
* If we haven't written the salt, do so now. Note that the
* feature may not be activated yet, but that's fine since
* the presence of this ZAP entry is backwards compatible.
*/
if (zap_contains(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
DMU_POOL_CHECKSUM_SALT) == ENOENT) {
VERIFY0(zap_add(spa->spa_meta_objset,
DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_CHECKSUM_SALT, 1,
sizeof (spa->spa_cksum_salt.zcs_bytes),
spa->spa_cksum_salt.zcs_bytes, tx));
}
rrw_exit(&dp->dp_config_rwlock, FTAG);
}

View File

@ -23,6 +23,7 @@
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#include <sys/zfs_context.h>
@ -50,7 +51,7 @@
#include <sys/arc.h>
#include <sys/ddt.h>
#include "zfs_prop.h"
#include "zfeature_common.h"
#include <sys/zfeature.h>
/*
* SPA locking
@ -556,6 +557,7 @@ spa_add(const char *name, nvlist_t *config, const char *altroot)
mutex_init(&spa->spa_history_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_proc_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_props_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_cksum_tmpls_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_scrub_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_suspend_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&spa->spa_vdev_top_lock, NULL, MUTEX_DEFAULT, NULL);
@ -716,6 +718,8 @@ spa_remove(spa_t *spa)
for (int t = 0; t < TXG_SIZE; t++)
bplist_destroy(&spa->spa_free_bplist[t]);
zio_checksum_templates_free(spa);
cv_destroy(&spa->spa_async_cv);
cv_destroy(&spa->spa_evicting_os_cv);
cv_destroy(&spa->spa_proc_cv);
@ -729,6 +733,7 @@ spa_remove(spa_t *spa)
mutex_destroy(&spa->spa_history_lock);
mutex_destroy(&spa->spa_proc_lock);
mutex_destroy(&spa->spa_props_lock);
mutex_destroy(&spa->spa_cksum_tmpls_lock);
mutex_destroy(&spa->spa_scrub_lock);
mutex_destroy(&spa->spa_suspend_lock);
mutex_destroy(&spa->spa_vdev_top_lock);

View File

@ -27,6 +27,7 @@
* Copyright 2013 DEY Storage Systems, Inc.
* Copyright 2014 HybridCluster. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
/* Portions Copyright 2010 Robert Milkowski */
@ -318,6 +319,7 @@ typedef struct dmu_buf {
#define DMU_POOL_FREE_BPOBJ "free_bpobj"
#define DMU_POOL_BPTREE_OBJ "bptree_obj"
#define DMU_POOL_EMPTY_BPOBJ "empty_bpobj"
#define DMU_POOL_CHECKSUM_SALT "org.illumos:checksum_salt"
/*
* Allocate an object from this objset. The range of object numbers

View File

@ -23,6 +23,7 @@
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#ifndef _SYS_SPA_H
@ -146,6 +147,14 @@ typedef struct zio_cksum {
uint64_t zc_word[4];
} zio_cksum_t;
/*
* Some checksums/hashes need a 256-bit initialization salt. This salt is kept
* secret and is suitable for use in MAC algorithms as the key.
*/
typedef struct zio_cksum_salt {
uint8_t zcs_bytes[32];
} zio_cksum_salt_t;
/*
* Each block is described by its DVAs, time of birth, checksum, etc.
* The word-by-word, bit-by-bit layout of the blkptr is as follows:

View File

@ -23,6 +23,7 @@
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#ifndef _SYS_SPA_IMPL_H
@ -165,6 +166,10 @@ struct spa {
uint64_t spa_syncing_txg; /* txg currently syncing */
bpobj_t spa_deferred_bpobj; /* deferred-free bplist */
bplist_t spa_free_bplist[TXG_SIZE]; /* bplist of stuff to free */
zio_cksum_salt_t spa_cksum_salt; /* secret salt for cksum */
/* checksum context templates */
kmutex_t spa_cksum_tmpls_lock;
void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
uberblock_t spa_ubsync; /* last synced uberblock */
uberblock_t spa_uberblock; /* current uberblock */
boolean_t spa_extreme_rewind; /* rewind past deferred frees */

View File

@ -82,6 +82,9 @@ enum zio_checksum {
ZIO_CHECKSUM_SHA256,
ZIO_CHECKSUM_ZILOG2,
ZIO_CHECKSUM_NOPARITY,
ZIO_CHECKSUM_SHA512,
ZIO_CHECKSUM_SKEIN,
ZIO_CHECKSUM_EDONR,
ZIO_CHECKSUM_FUNCTIONS
};

View File

@ -20,13 +20,15 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014 by Delphix. All rights reserved.
* Copyright (c) 2014, 2015 by Delphix. All rights reserved.
* Copyright Saso Kiselkov 2013, All rights reserved.
*/
#ifndef _SYS_ZIO_CHECKSUM_H
#define _SYS_ZIO_CHECKSUM_H
#include <sys/zio.h>
#include <zfeature_common.h>
#ifdef __cplusplus
extern "C" {
@ -35,17 +37,34 @@ extern "C" {
/*
* Signature for checksum functions.
*/
typedef void zio_checksum_func_t(const void *, uint64_t, zio_cksum_t *);
typedef void zio_checksum_t(const void *data, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp);
typedef void *zio_checksum_tmpl_init_t(const zio_cksum_salt_t *salt);
typedef void zio_checksum_tmpl_free_t(void *ctx_template);
typedef enum zio_checksum_flags {
/* Strong enough for metadata? */
ZCHECKSUM_FLAG_METADATA = (1 << 1),
/* ZIO embedded checksum */
ZCHECKSUM_FLAG_EMBEDDED = (1 << 2),
/* Strong enough for dedup (without verification)? */
ZCHECKSUM_FLAG_DEDUP = (1 << 3),
/* Uses salt value */
ZCHECKSUM_FLAG_SALTED = (1 << 4),
/* Strong enough for nopwrite? */
ZCHECKSUM_FLAG_NOPWRITE = (1 << 5)
} zio_checksum_flags_t;
/*
* Information about each checksum function.
*/
typedef struct zio_checksum_info {
zio_checksum_func_t *ci_func[2]; /* checksum function per byteorder */
int ci_correctable; /* number of correctable bits */
int ci_eck; /* uses zio embedded checksum? */
boolean_t ci_dedup; /* strong enough for dedup? */
char *ci_name; /* descriptive name */
/* checksum function for each byteorder */
zio_checksum_t *ci_func[2];
zio_checksum_tmpl_init_t *ci_tmpl_init;
zio_checksum_tmpl_free_t *ci_tmpl_free;
zio_checksum_flags_t ci_flags;
char *ci_name; /* descriptive name */
} zio_checksum_info_t;
typedef struct zio_bad_cksum {
@ -62,12 +81,28 @@ extern zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS];
/*
* Checksum routines.
*/
extern zio_checksum_func_t zio_checksum_SHA256;
extern zio_checksum_t zio_checksum_SHA256;
extern zio_checksum_t zio_checksum_SHA512_native;
extern zio_checksum_t zio_checksum_SHA512_byteswap;
/* Skein */
extern zio_checksum_t zio_checksum_skein_native;
extern zio_checksum_t zio_checksum_skein_byteswap;
extern zio_checksum_tmpl_init_t zio_checksum_skein_tmpl_init;
extern zio_checksum_tmpl_free_t zio_checksum_skein_tmpl_free;
/* Edon-R */
extern zio_checksum_t zio_checksum_edonr_native;
extern zio_checksum_t zio_checksum_edonr_byteswap;
extern zio_checksum_tmpl_init_t zio_checksum_edonr_tmpl_init;
extern zio_checksum_tmpl_free_t zio_checksum_edonr_tmpl_free;
extern void zio_checksum_compute(zio_t *zio, enum zio_checksum checksum,
void *data, uint64_t size);
extern int zio_checksum_error(zio_t *zio, zio_bad_cksum_t *out);
extern enum zio_checksum spa_dedup_checksum(spa_t *spa);
extern void zio_checksum_templates_free(spa_t *spa);
extern spa_feature_t zio_checksum_to_feature(enum zio_checksum cksum);
#ifdef __cplusplus
}

View File

@ -180,6 +180,7 @@
#include <sys/dsl_bookmark.h>
#include <sys/dsl_userhold.h>
#include <sys/zfeature.h>
#include <sys/zio_checksum.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
@ -3817,11 +3818,6 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
return (SET_ERROR(ENOTSUP));
break;
case ZFS_PROP_DEDUP:
if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
return (SET_ERROR(ENOTSUP));
break;
case ZFS_PROP_RECORDSIZE:
/* Record sizes above 128k need the feature to be enabled */
if (nvpair_value_uint64(pair, &intval) == 0 &&
@ -3872,6 +3868,45 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
return (SET_ERROR(ENOTSUP));
}
break;
case ZFS_PROP_CHECKSUM:
case ZFS_PROP_DEDUP:
{
spa_feature_t feature;
spa_t *spa;
/* dedup feature version checks */
if (prop == ZFS_PROP_DEDUP &&
zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
return (SET_ERROR(ENOTSUP));
if (nvpair_value_uint64(pair, &intval) != 0)
return (SET_ERROR(EINVAL));
/* check prop value is enabled in features */
feature = zio_checksum_to_feature(intval);
if (feature == SPA_FEATURE_NONE)
break;
if ((err = spa_open(dsname, &spa, FTAG)) != 0)
return (err);
/*
* Salted checksums are not supported on root pools.
*/
if (spa_bootfs(spa) != 0 &&
intval < ZIO_CHECKSUM_FUNCTIONS &&
(zio_checksum_table[intval].ci_flags &
ZCHECKSUM_FLAG_SALTED)) {
spa_close(spa, FTAG);
return (SET_ERROR(ERANGE));
}
if (!spa_feature_is_enabled(spa, feature)) {
spa_close(spa, FTAG);
return (SET_ERROR(ENOTSUP));
}
spa_close(spa, FTAG);
break;
}
}
return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));

View File

@ -929,7 +929,7 @@ zio_write_phys(zio_t *pio, vdev_t *vd, uint64_t offset, uint64_t size,
zio->io_prop.zp_checksum = checksum;
if (zio_checksum_table[checksum].ci_eck) {
if (zio_checksum_table[checksum].ci_flags & ZCHECKSUM_FLAG_EMBEDDED) {
/*
* zec checksums are necessarily destructive -- they modify
* the end of the write buffer to hold the verifier/checksum.
@ -1125,8 +1125,8 @@ zio_write_bp_init(zio_t *zio)
if (BP_IS_HOLE(bp) || !zp->zp_dedup)
return (ZIO_PIPELINE_CONTINUE);
ASSERT(zio_checksum_table[zp->zp_checksum].ci_dedup ||
zp->zp_dedup_verify);
ASSERT((zio_checksum_table[zp->zp_checksum].ci_flags &
ZCHECKSUM_FLAG_DEDUP) || zp->zp_dedup_verify);
if (BP_GET_CHECKSUM(bp) == zp->zp_checksum) {
BP_SET_DEDUP(bp, 1);
@ -1981,12 +1981,22 @@ zio_write_gang_block(zio_t *pio)
}
/*
* The zio_nop_write stage in the pipeline determines if allocating
* a new bp is necessary. By leveraging a cryptographically secure checksum,
* such as SHA256, we can compare the checksums of the new data and the old
* to determine if allocating a new block is required. The nopwrite
* feature can handle writes in either syncing or open context (i.e. zil
* writes) and as a result is mutually exclusive with dedup.
* The zio_nop_write stage in the pipeline determines if allocating a
* new bp is necessary. The nopwrite feature can handle writes in
* either syncing or open context (i.e. zil writes) and as a result is
* mutually exclusive with dedup.
*
* By leveraging a cryptographically secure checksum, such as SHA256, we
* can compare the checksums of the new data and the old to determine if
* allocating a new block is required. Note that our requirements for
* cryptographic strength are fairly weak: there can't be any accidental
* hash collisions, but we don't need to be secure against intentional
* (malicious) collisions. To trigger a nopwrite, you have to be able
* to write the file to begin with, and triggering an incorrect (hash
* collision) nopwrite is no worse than simply writing to the file.
* That said, there are no known attacks against the checksum algorithms
* used for nopwrite, assuming that the salt and the checksums
* themselves remain secret.
*/
static int
zio_nop_write(zio_t *zio)
@ -2009,7 +2019,8 @@ zio_nop_write(zio_t *zio)
* allocate a new bp.
*/
if (BP_IS_HOLE(bp_orig) ||
!zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_dedup ||
!(zio_checksum_table[BP_GET_CHECKSUM(bp)].ci_flags &
ZCHECKSUM_FLAG_NOPWRITE) ||
BP_GET_CHECKSUM(bp) != BP_GET_CHECKSUM(bp_orig) ||
BP_GET_COMPRESS(bp) != BP_GET_COMPRESS(bp_orig) ||
BP_GET_DEDUP(bp) != BP_GET_DEDUP(bp_orig) ||
@ -2021,7 +2032,8 @@ zio_nop_write(zio_t *zio)
* avoid allocating a new bp and issuing any I/O.
*/
if (ZIO_CHECKSUM_EQUAL(bp->blk_cksum, bp_orig->blk_cksum)) {
ASSERT(zio_checksum_table[zp->zp_checksum].ci_dedup);
ASSERT(zio_checksum_table[zp->zp_checksum].ci_flags &
ZCHECKSUM_FLAG_NOPWRITE);
ASSERT3U(BP_GET_PSIZE(bp), ==, BP_GET_PSIZE(bp_orig));
ASSERT3U(BP_GET_LSIZE(bp), ==, BP_GET_LSIZE(bp_orig));
ASSERT(zp->zp_compress != ZIO_COMPRESS_OFF);
@ -2302,7 +2314,8 @@ zio_ddt_write(zio_t *zio)
* we can't resolve it, so just convert to an ordinary write.
* (And automatically e-mail a paper to Nature?)
*/
if (!zio_checksum_table[zp->zp_checksum].ci_dedup) {
if (!(zio_checksum_table[zp->zp_checksum].ci_flags &
ZCHECKSUM_FLAG_DEDUP)) {
zp->zp_checksum = spa_dedup_checksum(spa);
zio_pop_transforms(zio);
zio->io_stage = ZIO_STAGE_OPEN;

View File

@ -22,10 +22,12 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
#include <sys/zfs_context.h>
#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/zio.h>
#include <sys/zio_checksum.h>
#include <sys/zil.h>
@ -59,29 +61,95 @@
* checksum function of the appropriate strength. When reading a block,
* we compare the expected checksum against the actual checksum, which we
* compute via the checksum function specified by BP_GET_CHECKSUM(bp).
*
* SALTED CHECKSUMS
*
* To enable the use of less secure hash algorithms with dedup, we
* introduce the notion of salted checksums (MACs, really). A salted
* checksum is fed both a random 256-bit value (the salt) and the data
* to be checksummed. This salt is kept secret (stored on the pool, but
* never shown to the user). Thus even if an attacker knew of collision
* weaknesses in the hash algorithm, they won't be able to mount a known
* plaintext attack on the DDT, since the actual hash value cannot be
* known ahead of time. How the salt is used is algorithm-specific
* (some might simply prefix it to the data block, others might need to
* utilize a full-blown HMAC). On disk the salt is stored in a ZAP
* object in the MOS (DMU_POOL_CHECKSUM_SALT).
*
* CONTEXT TEMPLATES
*
* Some hashing algorithms need to perform a substantial amount of
* initialization work (e.g. salted checksums above may need to pre-hash
* the salt) before being able to process data. Performing this
* redundant work for each block would be wasteful, so we instead allow
* a checksum algorithm to do the work once (the first time it's used)
* and then keep this pre-initialized context as a template inside the
* spa_t (spa_cksum_tmpls). If the zio_checksum_info_t contains
* non-NULL ci_tmpl_init and ci_tmpl_free callbacks, they are used to
* construct and destruct the pre-initialized checksum context. The
* pre-initialized context is then reused during each checksum
* invocation and passed to the checksum function.
*/
/*ARGSUSED*/
static void
zio_checksum_off(const void *buf, uint64_t size, zio_cksum_t *zcp)
zio_checksum_off(const void *buf, uint64_t size,
const void *ctx_template, zio_cksum_t *zcp)
{
ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0);
}
zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
{{NULL, NULL}, 0, 0, 0, "inherit"},
{{NULL, NULL}, 0, 0, 0, "on"},
{{zio_checksum_off, zio_checksum_off}, 0, 0, 0, "off"},
{{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, 0, "label"},
{{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 1, 0, "gang_header"},
{{fletcher_2_native, fletcher_2_byteswap}, 0, 1, 0, "zilog"},
{{fletcher_2_native, fletcher_2_byteswap}, 0, 0, 0, "fletcher2"},
{{fletcher_4_native, fletcher_4_byteswap}, 1, 0, 0, "fletcher4"},
{{zio_checksum_SHA256, zio_checksum_SHA256}, 1, 0, 1, "sha256"},
{{fletcher_4_native, fletcher_4_byteswap}, 0, 1, 0, "zilog2"},
{{zio_checksum_off, zio_checksum_off}, 0, 0, 0, "noparity"},
{{NULL, NULL}, NULL, NULL, 0, "inherit"},
{{NULL, NULL}, NULL, NULL, 0, "on"},
{{zio_checksum_off, zio_checksum_off},
NULL, NULL, 0, "off"},
{{zio_checksum_SHA256, zio_checksum_SHA256},
NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_EMBEDDED,
"label"},
{{zio_checksum_SHA256, zio_checksum_SHA256},
NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_EMBEDDED,
"gang_header"},
{{fletcher_2_native, fletcher_2_byteswap},
NULL, NULL, ZCHECKSUM_FLAG_EMBEDDED, "zilog"},
{{fletcher_2_native, fletcher_2_byteswap},
NULL, NULL, 0, "fletcher2"},
{{fletcher_4_native, fletcher_4_byteswap},
NULL, NULL, ZCHECKSUM_FLAG_METADATA, "fletcher4"},
{{zio_checksum_SHA256, zio_checksum_SHA256},
NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP |
ZCHECKSUM_FLAG_NOPWRITE, "sha256"},
{{fletcher_4_native, fletcher_4_byteswap},
NULL, NULL, ZCHECKSUM_FLAG_EMBEDDED, "zilog2"},
{{zio_checksum_off, zio_checksum_off},
NULL, NULL, 0, "noparity"},
{{zio_checksum_SHA512_native, zio_checksum_SHA512_byteswap},
NULL, NULL, ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP |
ZCHECKSUM_FLAG_NOPWRITE, "sha512"},
{{zio_checksum_skein_native, zio_checksum_skein_byteswap},
zio_checksum_skein_tmpl_init, zio_checksum_skein_tmpl_free,
ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_DEDUP |
ZCHECKSUM_FLAG_SALTED | ZCHECKSUM_FLAG_NOPWRITE, "skein"},
{{zio_checksum_edonr_native, zio_checksum_edonr_byteswap},
zio_checksum_edonr_tmpl_init, zio_checksum_edonr_tmpl_free,
ZCHECKSUM_FLAG_METADATA | ZCHECKSUM_FLAG_SALTED |
ZCHECKSUM_FLAG_NOPWRITE, "edonr"},
};
spa_feature_t
zio_checksum_to_feature(enum zio_checksum cksum)
{
switch (cksum) {
case ZIO_CHECKSUM_SHA512:
return (SPA_FEATURE_SHA512);
case ZIO_CHECKSUM_SKEIN:
return (SPA_FEATURE_SKEIN);
case ZIO_CHECKSUM_EDONR:
return (SPA_FEATURE_EDONR);
}
return (SPA_FEATURE_NONE);
}
enum zio_checksum
zio_checksum_select(enum zio_checksum child, enum zio_checksum parent)
{
@ -115,7 +183,8 @@ zio_checksum_dedup_select(spa_t *spa, enum zio_checksum child,
if (child == (ZIO_CHECKSUM_ON | ZIO_CHECKSUM_VERIFY))
return (spa_dedup_checksum(spa) | ZIO_CHECKSUM_VERIFY);
ASSERT(zio_checksum_table[child & ZIO_CHECKSUM_MASK].ci_dedup ||
ASSERT((zio_checksum_table[child & ZIO_CHECKSUM_MASK].ci_flags &
ZCHECKSUM_FLAG_DEDUP) ||
(child & ZIO_CHECKSUM_VERIFY) || child == ZIO_CHECKSUM_OFF);
return (child);
@ -147,6 +216,30 @@ zio_checksum_label_verifier(zio_cksum_t *zcp, uint64_t offset)
ZIO_SET_CHECKSUM(zcp, offset, 0, 0, 0);
}
/*
* Calls the template init function of a checksum which supports context
* templates and installs the template into the spa_t.
*/
static void
zio_checksum_template_init(enum zio_checksum checksum, spa_t *spa)
{
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
if (ci->ci_tmpl_init == NULL)
return;
if (spa->spa_cksum_tmpls[checksum] != NULL)
return;
VERIFY(ci->ci_tmpl_free != NULL);
mutex_enter(&spa->spa_cksum_tmpls_lock);
if (spa->spa_cksum_tmpls[checksum] == NULL) {
spa->spa_cksum_tmpls[checksum] =
ci->ci_tmpl_init(&spa->spa_cksum_salt);
VERIFY(spa->spa_cksum_tmpls[checksum] != NULL);
}
mutex_exit(&spa->spa_cksum_tmpls_lock);
}
/*
* Generate the checksum.
*/
@ -158,11 +251,14 @@ zio_checksum_compute(zio_t *zio, enum zio_checksum checksum,
uint64_t offset = zio->io_offset;
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
zio_cksum_t cksum;
spa_t *spa = zio->io_spa;
ASSERT((uint_t)checksum < ZIO_CHECKSUM_FUNCTIONS);
ASSERT(ci->ci_func[0] != NULL);
if (ci->ci_eck) {
zio_checksum_template_init(checksum, spa);
if (ci->ci_flags & ZCHECKSUM_FLAG_EMBEDDED) {
zio_eck_t *eck;
if (checksum == ZIO_CHECKSUM_ZILOG2) {
@ -181,10 +277,12 @@ zio_checksum_compute(zio_t *zio, enum zio_checksum checksum,
else
bp->blk_cksum = eck->zec_cksum;
eck->zec_magic = ZEC_MAGIC;
ci->ci_func[0](data, size, &cksum);
ci->ci_func[0](data, size, spa->spa_cksum_tmpls[checksum],
&cksum);
eck->zec_cksum = cksum;
} else {
ci->ci_func[0](data, size, &bp->blk_cksum);
ci->ci_func[0](data, size, spa->spa_cksum_tmpls[checksum],
&bp->blk_cksum);
}
}
@ -202,11 +300,14 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info)
void *data = zio->io_data;
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
zio_cksum_t actual_cksum, expected_cksum, verifier;
spa_t *spa = zio->io_spa;
if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func[0] == NULL)
return (SET_ERROR(EINVAL));
if (ci->ci_eck) {
zio_checksum_template_init(checksum, spa);
if (ci->ci_flags & ZCHECKSUM_FLAG_EMBEDDED) {
zio_eck_t *eck;
if (checksum == ZIO_CHECKSUM_ZILOG2) {
@ -243,7 +344,8 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info)
expected_cksum = eck->zec_cksum;
eck->zec_cksum = verifier;
ci->ci_func[byteswap](data, size, &actual_cksum);
ci->ci_func[byteswap](data, size,
spa->spa_cksum_tmpls[checksum], &actual_cksum);
eck->zec_cksum = expected_cksum;
if (byteswap)
@ -253,7 +355,8 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info)
ASSERT(!BP_IS_GANG(bp));
byteswap = BP_SHOULD_BYTESWAP(bp);
expected_cksum = bp->blk_cksum;
ci->ci_func[byteswap](data, size, &actual_cksum);
ci->ci_func[byteswap](data, size,
spa->spa_cksum_tmpls[checksum], &actual_cksum);
}
info->zbc_expected = expected_cksum;
@ -275,3 +378,23 @@ zio_checksum_error(zio_t *zio, zio_bad_cksum_t *info)
return (0);
}
/*
* Called by a spa_t that's about to be deallocated. This steps through
* all of the checksum context templates and deallocates any that were
* initialized using the algorithm-specific template init function.
*/
void
zio_checksum_templates_free(spa_t *spa)
{
for (enum zio_checksum checksum = 0;
checksum < ZIO_CHECKSUM_FUNCTIONS; checksum++) {
if (spa->spa_cksum_tmpls[checksum] != NULL) {
zio_checksum_info_t *ci = &zio_checksum_table[checksum];
VERIFY(ci->ci_tmpl_free != NULL);
ci->ci_tmpl_free(spa->spa_cksum_tmpls[checksum]);
spa->spa_cksum_tmpls[checksum] = NULL;
}
}
}

View File

@ -27,6 +27,7 @@
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright 2013 Saso Kiselkov. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@ -124,6 +125,14 @@ _NOTE(CONSTCOND) } while (0)
#define ASSERT0(x) ((void)0)
#endif
/*
* Compile-time assertion. The condition 'x' must be constant.
*/
#define CTASSERT(x) _CTASSERT(x, __LINE__)
#define _CTASSERT(x, y) __CTASSERT(x, y)
#define __CTASSERT(x, y) \
typedef char __compile_time_assertion__ ## y [(x) ? 1 : -1]
#ifdef _KERNEL
extern void abort_sequence_enter(char *);

93
uts/common/sys/edonr.h Normal file
View File

@ -0,0 +1,93 @@
/*
* IDI,NTNU
*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright (C) 2009, 2010, Jorn Amundsen <jorn.amundsen@ntnu.no>
*
* Tweaked Edon-R implementation for SUPERCOP, based on NIST API.
*
* $Id: edonr.h 517 2013-02-17 20:34:39Z joern $
*/
/*
* Portions copyright (c) 2013, Saso Kiselkov, All rights reserved
*/
#ifndef _SYS_EDONR_H_
#define _SYS_EDONR_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
/*
* EdonR allows to call EdonRUpdate() consecutively only if the total length
* of stored unprocessed data and the new supplied data is less than or equal
* to the BLOCK_SIZE on which the compression functions operates.
* Otherwise an assertion failure is invoked.
*/
/* Specific algorithm definitions */
#define EdonR224_DIGEST_SIZE 28
#define EdonR224_BLOCK_SIZE 64
#define EdonR256_DIGEST_SIZE 32
#define EdonR256_BLOCK_SIZE 64
#define EdonR384_DIGEST_SIZE 48
#define EdonR384_BLOCK_SIZE 128
#define EdonR512_DIGEST_SIZE 64
#define EdonR512_BLOCK_SIZE 128
#define EdonR256_BLOCK_BITSIZE 512
#define EdonR512_BLOCK_BITSIZE 1024
typedef struct {
uint32_t DoublePipe[16];
uint8_t LastPart[EdonR256_BLOCK_SIZE * 2];
} EdonRData256;
typedef struct {
uint64_t DoublePipe[16];
uint8_t LastPart[EdonR512_BLOCK_SIZE * 2];
} EdonRData512;
typedef struct {
size_t hashbitlen;
/* + algorithm specific parameters */
int unprocessed_bits;
uint64_t bits_processed;
union {
EdonRData256 p256[1];
EdonRData512 p512[1];
} pipe[1];
} EdonRState;
void EdonRInit(EdonRState *state, size_t hashbitlen);
void EdonRUpdate(EdonRState *state, const uint8_t *data, size_t databitlen);
void EdonRFinal(EdonRState *state, uint8_t *hashval);
void EdonRHash(size_t hashbitlen, const uint8_t *data, size_t databitlen,
uint8_t *hashval);
#ifdef __cplusplus
}
#endif
#endif /* _SYS_EDONR_H_ */

178
uts/common/sys/skein.h Normal file
View File

@ -0,0 +1,178 @@
/*
* Interface declarations for Skein hashing.
* Source code author: Doug Whiting, 2008.
* This algorithm and source code is released to the public domain.
*
* The following compile-time switches may be defined to control some
* tradeoffs between speed, code size, error checking, and security.
*
* The "default" note explains what happens when the switch is not defined.
*
* SKEIN_DEBUG -- make callouts from inside Skein code
* to examine/display intermediate values.
* [default: no callouts (no overhead)]
*
* SKEIN_ERR_CHECK -- how error checking is handled inside Skein
* code. If not defined, most error checking
* is disabled (for performance). Otherwise,
* the switch value is interpreted as:
* 0: use assert() to flag errors
* 1: return SKEIN_FAIL to flag errors
*/
/* Copyright 2013 Doug Whiting. This code is released to the public domain. */
#ifndef _SYS_SKEIN_H_
#define _SYS_SKEIN_H_
#include <sys/types.h> /* get size_t definition */
#ifdef __cplusplus
extern "C" {
#endif
enum {
SKEIN_SUCCESS = 0, /* return codes from Skein calls */
SKEIN_FAIL = 1,
SKEIN_BAD_HASHLEN = 2
};
#define SKEIN_MODIFIER_WORDS (2) /* number of modifier (tweak) words */
#define SKEIN_256_STATE_WORDS (4)
#define SKEIN_512_STATE_WORDS (8)
#define SKEIN1024_STATE_WORDS (16)
#define SKEIN_MAX_STATE_WORDS (16)
#define SKEIN_256_STATE_BYTES (8 * SKEIN_256_STATE_WORDS)
#define SKEIN_512_STATE_BYTES (8 * SKEIN_512_STATE_WORDS)
#define SKEIN1024_STATE_BYTES (8 * SKEIN1024_STATE_WORDS)
#define SKEIN_256_STATE_BITS (64 * SKEIN_256_STATE_WORDS)
#define SKEIN_512_STATE_BITS (64 * SKEIN_512_STATE_WORDS)
#define SKEIN1024_STATE_BITS (64 * SKEIN1024_STATE_WORDS)
#define SKEIN_256_BLOCK_BYTES (8 * SKEIN_256_STATE_WORDS)
#define SKEIN_512_BLOCK_BYTES (8 * SKEIN_512_STATE_WORDS)
#define SKEIN1024_BLOCK_BYTES (8 * SKEIN1024_STATE_WORDS)
typedef struct {
size_t hashBitLen; /* size of hash result, in bits */
size_t bCnt; /* current byte count in buffer b[] */
/* tweak words: T[0]=byte cnt, T[1]=flags */
uint64_t T[SKEIN_MODIFIER_WORDS];
} Skein_Ctxt_Hdr_t;
typedef struct { /* 256-bit Skein hash context structure */
Skein_Ctxt_Hdr_t h; /* common header context variables */
uint64_t X[SKEIN_256_STATE_WORDS]; /* chaining variables */
/* partial block buffer (8-byte aligned) */
uint8_t b[SKEIN_256_BLOCK_BYTES];
} Skein_256_Ctxt_t;
typedef struct { /* 512-bit Skein hash context structure */
Skein_Ctxt_Hdr_t h; /* common header context variables */
uint64_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */
/* partial block buffer (8-byte aligned) */
uint8_t b[SKEIN_512_BLOCK_BYTES];
} Skein_512_Ctxt_t;
typedef struct { /* 1024-bit Skein hash context structure */
Skein_Ctxt_Hdr_t h; /* common header context variables */
uint64_t X[SKEIN1024_STATE_WORDS]; /* chaining variables */
/* partial block buffer (8-byte aligned) */
uint8_t b[SKEIN1024_BLOCK_BYTES];
} Skein1024_Ctxt_t;
/* Skein APIs for (incremental) "straight hashing" */
int Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen);
int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen);
int Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen);
int Skein_256_Update(Skein_256_Ctxt_t *ctx, const uint8_t *msg,
size_t msgByteCnt);
int Skein_512_Update(Skein_512_Ctxt_t *ctx, const uint8_t *msg,
size_t msgByteCnt);
int Skein1024_Update(Skein1024_Ctxt_t *ctx, const uint8_t *msg,
size_t msgByteCnt);
int Skein_256_Final(Skein_256_Ctxt_t *ctx, uint8_t *hashVal);
int Skein_512_Final(Skein_512_Ctxt_t *ctx, uint8_t *hashVal);
int Skein1024_Final(Skein1024_Ctxt_t *ctx, uint8_t *hashVal);
/*
* Skein APIs for "extended" initialization: MAC keys, tree hashing.
* After an InitExt() call, just use Update/Final calls as with Init().
*
* Notes: Same parameters as _Init() calls, plus treeInfo/key/keyBytes.
* When keyBytes == 0 and treeInfo == SKEIN_SEQUENTIAL,
* the results of InitExt() are identical to calling Init().
* The function Init() may be called once to "precompute" the IV for
* a given hashBitLen value, then by saving a copy of the context
* the IV computation may be avoided in later calls.
* Similarly, the function InitExt() may be called once per MAC key
* to precompute the MAC IV, then a copy of the context saved and
* reused for each new MAC computation.
*/
int Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen,
uint64_t treeInfo, const uint8_t *key, size_t keyBytes);
int Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen,
uint64_t treeInfo, const uint8_t *key, size_t keyBytes);
int Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen,
uint64_t treeInfo, const uint8_t *key, size_t keyBytes);
/*
* Skein APIs for MAC and tree hash:
* Final_Pad: pad, do final block, but no OUTPUT type
* Output: do just the output stage
*/
int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, uint8_t *hashVal);
int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, uint8_t *hashVal);
int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, uint8_t *hashVal);
#ifndef SKEIN_TREE_HASH
#define SKEIN_TREE_HASH (1)
#endif
#if SKEIN_TREE_HASH
int Skein_256_Output(Skein_256_Ctxt_t *ctx, uint8_t *hashVal);
int Skein_512_Output(Skein_512_Ctxt_t *ctx, uint8_t *hashVal);
int Skein1024_Output(Skein1024_Ctxt_t *ctx, uint8_t *hashVal);
#endif
/*
* When you initialize a Skein KCF hashing method you can pass this param
* structure in cm_param to fine-tune the algorithm's defaults.
*/
typedef struct skein_param {
size_t sp_digest_bitlen; /* length of digest in bits */
} skein_param_t;
/* Module definitions */
#ifdef SKEIN_MODULE_IMPL
#define CKM_SKEIN_256 "CKM_SKEIN_256"
#define CKM_SKEIN_512 "CKM_SKEIN_512"
#define CKM_SKEIN1024 "CKM_SKEIN1024"
#define CKM_SKEIN_256_MAC "CKM_SKEIN_256_MAC"
#define CKM_SKEIN_512_MAC "CKM_SKEIN_512_MAC"
#define CKM_SKEIN1024_MAC "CKM_SKEIN1024_MAC"
typedef enum skein_mech_type {
SKEIN_256_MECH_INFO_TYPE,
SKEIN_512_MECH_INFO_TYPE,
SKEIN1024_MECH_INFO_TYPE,
SKEIN_256_MAC_MECH_INFO_TYPE,
SKEIN_512_MAC_MECH_INFO_TYPE,
SKEIN1024_MAC_MECH_INFO_TYPE
} skein_mech_type_t;
#define VALID_SKEIN_DIGEST_MECH(__mech) \
((int)(__mech) >= SKEIN_256_MECH_INFO_TYPE && \
(__mech) <= SKEIN1024_MECH_INFO_TYPE)
#define VALID_SKEIN_MAC_MECH(__mech) \
((int)(__mech) >= SKEIN_256_MAC_MECH_INFO_TYPE && \
(__mech) <= SKEIN1024_MAC_MECH_INFO_TYPE)
#endif /* SKEIN_MODULE_IMPL */
#ifdef __cplusplus
}
#endif
#endif /* _SYS_SKEIN_H_ */