hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

blowfish.ha (7553B)


      1 // SPDX-License-Identifier: MIT
      2 // (c) Hare authors <https://harelang.org>
      3 // (c) 2010 The Go Authors. All rights reserved.
      4 
      5 use bytes;
      6 use crypto::cipher;
      7 
      8 // The block size of the Blowfish cipher in bytes.
      9 export def BLOCKSZ: size = 8;
     10 
     11 export type state = struct {
     12 	block: cipher::block,
     13 	p: [18]u32,
     14 	s0: [256]u32,
     15 	s1: [256]u32,
     16 	s2: [256]u32,
     17 	s3: [256]u32,
     18 };
     19 
     20 const vtable: cipher::blockvtable = cipher::blockvtable {
     21 	blocksz = BLOCKSZ,
     22 	nparallel = 1,
     23 	encrypt = &block_encrypt,
     24 	decrypt = &block_decrypt,
     25 	finish = &block_finish,
     26 };
     27 
     28 // Initializes a new Blowfish cipher. The user should must call [[init]] or
     29 // [[init_salt]] prior to use, then may use [[crypto::cipher::encrypt]] et al.
     30 // The user must call [[finish]] when they are done using the stream to securely
     31 // erase secret information stored in the stream state.
     32 export fn new() state = {
     33 	return state {
     34 		block = &vtable,
     35 		p = p,
     36 		s0 = s0,
     37 		s1 = s1,
     38 		s2 = s2,
     39 		s3 = s3,
     40 	};
     41 };
     42 
     43 // Performs key expansion for a Blowfish cipher.
     44 export fn init(c: *state, key: []u8) void = {
     45 	let j = 0z;
     46 	for (let i = 0z; i < len(c.p); i += 1) {
     47 		c.p[i] ^= getword(key, &j);
     48 	};
     49 
     50 	let l = 0u32, r = 0u32;
     51 	init_vector(c, &l, &r, c.p);
     52 	init_vector(c, &l, &r, c.s0);
     53 	init_vector(c, &l, &r, c.s1);
     54 	init_vector(c, &l, &r, c.s2);
     55 	init_vector(c, &l, &r, c.s3);
     56 };
     57 
     58 fn init_vector(c: *state, l: *u32, r: *u32, vec: []u32) void = {
     59 	for (let i = 0z; i < len(vec); i += 2) {
     60 		const (v0, v1) = encrypt(c, *l, *r);
     61 		*l = v0;
     62 		*r = v1;
     63 		vec[i] = *l;
     64 		vec[i+1] = *r;
     65 	};
     66 };
     67 
     68 // Performs salted key expansion for a Blowfish cipher.
     69 export fn init_salt(c: *state, key: []u8, salt: []u8) void = {
     70 	if (len(salt) == 0) {
     71 		init(c, key);
     72 		return;
     73 	};
     74 
     75 	assert(len(key) >= 1, "Invalid blowfish key size");
     76 	let j = 0z;
     77 	for (let i = 0z; i < 18; i += 1) {
     78 		c.p[i] ^= getword(key, &j);
     79 	};
     80 
     81 	j = 0;
     82 	let l = 0u32, r = 0u32;
     83 	init_vector_salt(c, &l, &r, c.p, salt, &j);
     84 	init_vector_salt(c, &l, &r, c.s0, salt, &j);
     85 	init_vector_salt(c, &l, &r, c.s1, salt, &j);
     86 	init_vector_salt(c, &l, &r, c.s2, salt, &j);
     87 	init_vector_salt(c, &l, &r, c.s3, salt, &j);
     88 };
     89 
     90 fn init_vector_salt(
     91 	c: *state,
     92 	l: *u32,
     93 	r: *u32,
     94 	vec: []u32,
     95 	salt: []u8,
     96 	j: *size,
     97 ) void = {
     98 	for (let i = 0z; i < len(vec); i += 2) {
     99 		*l ^= getword(salt, j);
    100 		*r ^= getword(salt, j);
    101 		const (v0, v1) = encrypt(c, *l, *r);
    102 		*l = v0;
    103 		*r = v1;
    104 		vec[i] = *l;
    105 		vec[i+1] = *r;
    106 	};
    107 };
    108 
    109 fn block_encrypt(c: *cipher::block, dest: []u8, src: []u8) void = {
    110 	const c = c: *state;
    111 	assert(c.block.encrypt == &block_encrypt);
    112 
    113 	let l = src[0]<<24u32 | src[1]<<16u32 | src[2]<<8u32 | src[3]: u32;
    114 	let r = src[4]<<24u32 | src[5]<<16u32 | src[6]<<8u32 | src[7]: u32;
    115 	const (l, r) = encrypt(c, l, r);
    116 	dest[0] = (l>>24): u8;
    117 	dest[1] = (l>>16): u8;
    118 	dest[2] = (l>>8): u8;
    119 	dest[3] = l: u8;
    120 	dest[4] = (r>>24): u8;
    121 	dest[5] = (r>>16): u8;
    122 	dest[6] = (r>>8): u8;
    123 	dest[7] = r: u8;
    124 };
    125 
    126 fn block_decrypt(c: *cipher::block, dest: []u8, src: []u8) void = {
    127 	const c = c: *state;
    128 	assert(c.block.decrypt == &block_decrypt);
    129 
    130 	let l = src[0]<<24u32 | src[1]<<16u32 | src[2]<<8u32 | src[3]: u32;
    131 	let r = src[4]<<24u32 | src[5]<<16u32 | src[6]<<8u32 | src[7]: u32;
    132 	const v = decrypt(c, l, r);
    133 	l = v.0;
    134 	r = v.1;
    135 	dest[0] = (l>>24): u8;
    136 	dest[1] = (l>>16): u8;
    137 	dest[2] = (l>>8): u8;
    138 	dest[3] = l: u8;
    139 	dest[4] = (r>>24): u8;
    140 	dest[5] = (r>>16): u8;
    141 	dest[6] = (r>>8): u8;
    142 	dest[7] = r: u8;
    143 };
    144 
    145 fn block_finish(cipher: *cipher::block) void = {
    146 	const cipher = cipher: *state;
    147 	assert(cipher.block.finish == &block_finish);
    148 	bytes::zero((&cipher.p: *[*]u8)[..len(cipher.p) * size(u32)]);
    149 	bytes::zero((&cipher.s0: *[*]u8)[..len(cipher.s0) * size(u32)]);
    150 	bytes::zero((&cipher.s1: *[*]u8)[..len(cipher.s1) * size(u32)]);
    151 	bytes::zero((&cipher.s2: *[*]u8)[..len(cipher.s2) * size(u32)]);
    152 	bytes::zero((&cipher.s3: *[*]u8)[..len(cipher.s3) * size(u32)]);
    153 };
    154 
    155 fn encrypt(c: *state, l: u32, r: u32) (u32, u32) = {
    156 	let xl = l, xr = r;
    157 	xl ^= c.p[0];
    158 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[1];
    159 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[2];
    160 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[3];
    161 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[4];
    162 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[5];
    163 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[6];
    164 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[7];
    165 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[8];
    166 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[9];
    167 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[10];
    168 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[11];
    169 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[12];
    170 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[13];
    171 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[14];
    172 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[15];
    173 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[16];
    174 	xr ^= c.p[17];
    175 	return (xr, xl);
    176 };
    177 
    178 fn decrypt(c: *state, l: u32, r: u32) (u32, u32) = {
    179 	let xl = l, xr = r;
    180 	xl ^= c.p[17];
    181 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[16];
    182 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[15];
    183 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[14];
    184 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[13];
    185 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[12];
    186 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[11];
    187 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[10];
    188 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[9];
    189 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[8];
    190 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[7];
    191 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[6];
    192 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[5];
    193 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[4];
    194 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[3];
    195 	xr ^= ((c.s0[(xl>>24): u8] + c.s1[(xl>>16): u8]) ^ c.s2[(xl>>8): u8]) + c.s3[(xl): u8] ^ c.p[2];
    196 	xl ^= ((c.s0[(xr>>24): u8] + c.s1[(xr>>16): u8]) ^ c.s2[(xr>>8): u8]) + c.s3[(xr): u8] ^ c.p[1];
    197 	xr ^= c.p[0];
    198 	return (xr, xl);
    199 };
    200 
    201 // Gets the next word from b and updates the index, looping around in a circular
    202 // buffer.
    203 fn getword(b: []u8, i: *size) u32 = {
    204 	let j = *i;
    205 	let w = 0u32;
    206 	for (let i = 0; i < 4; i += 1) {
    207 		w = w<<8 | b[j]: u32;
    208 		j += 1;
    209 		if (j >= len(b)) {
    210 			j = 0;
    211 		};
    212 	};
    213 	*i = j;
    214 	return w;
    215 };