///|
priv struct BlowfishState {
p : Array[UInt]
s : Array[UInt]
}
///|
fn new_blowfish_state() -> BlowfishState {
{ p: init_p.copy(), s: init_s.copy() }
}
///|
fn blowfish_f(state : BlowfishState, x : UInt) -> UInt {
let a = ((x >> 24) & 0xffU).reinterpret_as_int()
let b = ((x >> 16) & 0xffU).reinterpret_as_int()
let c = ((x >> 8) & 0xffU).reinterpret_as_int()
let d = (x & 0xffU).reinterpret_as_int()
((state.s[a] + state.s[0x100 + b]) ^ state.s[0x200 + c]) + state.s[0x300 + d]
}
///|
fn blowfish_encrypt_block(
state : BlowfishState,
l0 : UInt,
r0 : UInt,
) -> (UInt, UInt) {
let mut l = l0
let mut r = r0
for i in 0..<16 {
l = l ^ state.p[i]
r = blowfish_f(state, l) ^ r
let t = l
l = r
r = t
}
let t = l
l = r
r = t
r = r ^ state.p[16]
l = l ^ state.p[17]
(l, r)
}
///|
fn stream_to_word(data : BytesView, offset : Int) -> (UInt, Int) {
let mut word = 0U
let mut off = offset
for _ in 0..<4 {
word = (word << 8) | data[off].to_uint()
off += 1
if off == data.length() {
off = 0
}
}
(word, off)
}
///|
fn blowfish_expand_key(
state : BlowfishState,
salt : BytesView,
key : BytesView,
) -> Unit {
let mut key_off = 0
for i in 0..<18 {
let (word, next_key_off) = stream_to_word(key, key_off)
state.p[i] = state.p[i] ^ word
key_off = next_key_off
}
let mut salt_off = 0
let mut l = 0U
let mut r = 0U
for i in 0..<9 {
let (salt_l, next_salt_off) = stream_to_word(salt, salt_off)
salt_off = next_salt_off
let (salt_r, next_salt_off2) = stream_to_word(salt, salt_off)
salt_off = next_salt_off2
l = l ^ salt_l
r = r ^ salt_r
let (new_l, new_r) = blowfish_encrypt_block(state, l, r)
l = new_l
r = new_r
state.p[i * 2] = l
state.p[i * 2 + 1] = r
}
for i in 0..<512 {
let (salt_l, next_salt_off) = stream_to_word(salt, salt_off)
salt_off = next_salt_off
let (salt_r, next_salt_off2) = stream_to_word(salt, salt_off)
salt_off = next_salt_off2
l = l ^ salt_l
r = r ^ salt_r
let (new_l, new_r) = blowfish_encrypt_block(state, l, r)
l = new_l
r = new_r
state.s[i * 2] = l
state.s[i * 2 + 1] = r
}
}
///|
fn blowfish_expand_zero_key(state : BlowfishState, key : BytesView) -> Unit {
let mut key_off = 0
for i in 0..<18 {
let (word, next_key_off) = stream_to_word(key, key_off)
state.p[i] = state.p[i] ^ word
key_off = next_key_off
}
let mut l = 0U
let mut r = 0U
for i in 0..<9 {
let (new_l, new_r) = blowfish_encrypt_block(state, l, r)
l = new_l
r = new_r
state.p[i * 2] = l
state.p[i * 2 + 1] = r
}
for i in 0..<512 {
let (new_l, new_r) = blowfish_encrypt_block(state, l, r)
l = new_l
r = new_r
state.s[i * 2] = l
state.s[i * 2 + 1] = r
}
}