hare

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

blowfish.ha (7565B)


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