const GCM_NB: usize = 4;
const GCM_ACCEPTING_HEADER: usize = 0;
const GCM_ACCEPTING_CIPHER: usize = 1;
const GCM_NOT_ACCEPTING_MORE: usize = 2;
const GCM_FINISHED: usize = 3;
use crate::aes;
use crate::aes::AES;
pub struct GCM {
table: [[u32; 4]; 128],
statex: [u8; 16],
y_0: [u8; 16],
lena: [u32; 2],
lenc: [u32; 2],
status: usize,
a: AES,
}
impl GCM {
fn pack(b: [u8; 4]) -> u32 {
((b[0] as u32) << 24)
| ((b[1] as u32) << 16)
| ((b[2] as u32) << 8)
| (b[3] as u32)
}
fn unpack(a: u32) -> [u8; 4] {
[
((a >> 24) & 0xff) as u8,
((a >> 16) & 0xff) as u8,
((a >> 8) & 0xff) as u8,
(a & 0xff) as u8,
]
}
fn precompute(&mut self, h: &[u8]) {
let mut b: [u8; 4] = [0; 4];
let mut j = 0;
for i in 0..GCM_NB {
b[0] = h[j];
b[1] = h[j + 1];
b[2] = h[j + 2];
b[3] = h[j + 3];
self.table[0][i] = GCM::pack(b);
j += 4;
}
for i in 1..128 {
let mut c: u32 = 0;
for j in 0..GCM_NB {
self.table[i][j] = c | (self.table[i - 1][j]) >> 1;
c = self.table[i - 1][j] << 31;
}
if c != 0 {
self.table[i][0] ^= 0xE1000000
}
}
}
fn gf2mul(&mut self) {
let mut p: [u32; 4] = [0; 4];
for i in 0..4 {
p[i] = 0
}
let mut j: usize = 8;
let mut m = 0;
for i in 0..128 {
j -= 1;
let mut c = ((self.statex[m] >> j) & 1) as u32;
c = (!c).wrapping_add(1); for k in 0..GCM_NB {
p[k] ^= self.table[i][k] & c
}
if j == 0 {
j = 8;
m += 1;
if m == 16 {
break;
}
}
}
j = 0;
for i in 0..GCM_NB {
let b = GCM::unpack(p[i]);
self.statex[j] = b[0];
self.statex[j + 1] = b[1];
self.statex[j + 2] = b[2];
self.statex[j + 3] = b[3];
j += 4;
}
}
fn wrap(&mut self) {
let mut f: [u32; 4] = [0; 4];
let mut el: [u8; 16] = [0; 16];
f[0] = (self.lena[0] << 3) | (self.lena[1] & 0xE0000000) >> 29;
f[1] = self.lena[1] << 3;
f[2] = (self.lenc[0] << 3) | (self.lenc[1] & 0xE0000000) >> 29;
f[3] = self.lenc[1] << 3;
let mut j = 0;
for i in 0..GCM_NB {
let b = GCM::unpack(f[i]);
el[j] = b[0];
el[j + 1] = b[1];
el[j + 2] = b[2];
el[j + 3] = b[3];
j += 4;
}
for i in 0..16 {
self.statex[i] ^= el[i]
}
self.gf2mul();
}
fn ghash(&mut self, plain: &[u8], len: usize) -> bool {
if self.status == GCM_ACCEPTING_HEADER {
self.status = GCM_ACCEPTING_CIPHER
}
if self.status != GCM_ACCEPTING_CIPHER {
return false;
}
let mut j = 0;
while j < len {
for i in 0..16 {
if j >= len {
break;
}
self.statex[i] ^= plain[j];
j += 1;
self.lenc[1] += 1;
if self.lenc[1] == 0 {
self.lenc[0] += 1
}
}
self.gf2mul();
}
if len % 16 != 0 {
self.status = GCM_NOT_ACCEPTING_MORE
}
true
}
pub fn init(&mut self, nk: usize, key: &[u8], niv: usize, iv: &[u8]) {
let mut h: [u8; 16] = [0; 16];
for i in 0..16 {
h[i] = 0;
self.statex[i] = 0
}
self.a = AES::new();
self.a.init(aes::ECB, nk, key, None);
self.a.ecb_encrypt(&mut h);
self.precompute(&h);
self.lena[0] = 0;
self.lenc[0] = 0;
self.lena[1] = 0;
self.lenc[1] = 0;
if niv == 12 {
for i in 0..12 {
self.a.f[i] = iv[i]
}
let b = GCM::unpack(1);
self.a.f[12] = b[0];
self.a.f[13] = b[1];
self.a.f[14] = b[2];
self.a.f[15] = b[3];
for i in 0..16 {
self.y_0[i] = self.a.f[i]
}
} else {
self.status = GCM_ACCEPTING_CIPHER;
self.ghash(iv, niv);
self.wrap();
for i in 0..16 {
self.a.f[i] = self.statex[i];
self.y_0[i] = self.a.f[i];
self.statex[i] = 0
}
self.lena[0] = 0;
self.lenc[0] = 0;
self.lena[1] = 0;
self.lenc[1] = 0;
}
self.status = GCM_ACCEPTING_HEADER;
}
pub fn new() -> GCM {
GCM {
table: [[0; 4]; 128],
statex: [0; 16],
y_0: [0; 16],
lena: [0; 2],
lenc: [0; 2],
status: 0,
a: AES::new(),
}
}
pub fn add_header(&mut self, header: &[u8], len: usize) -> bool {
if self.status != GCM_ACCEPTING_HEADER {
return false;
}
let mut j = 0;
while j < len {
for i in 0..16 {
if j >= len {
break;
}
self.statex[i] ^= header[j];
j += 1;
self.lena[1] += 1;
if self.lena[1] == 0 {
self.lena[0] += 1
}
}
self.gf2mul();
}
if len % 16 != 0 {
self.status = GCM_ACCEPTING_CIPHER
}
true
}
pub fn add_plain(&mut self, cipher: &mut [u8], plain: &[u8], len: usize) -> bool {
let mut cb: [u8; 16] = [0; 16];
let mut b: [u8; 4] = [0; 4];
let mut counter: u32;
if self.status == GCM_ACCEPTING_HEADER {
self.status = GCM_ACCEPTING_CIPHER
}
if self.status != GCM_ACCEPTING_CIPHER {
return false;
}
let mut j = 0;
while j < len {
b[0] = self.a.f[12];
b[1] = self.a.f[13];
b[2] = self.a.f[14];
b[3] = self.a.f[15];
counter = GCM::pack(b);
counter += 1;
b = GCM::unpack(counter);
self.a.f[12] = b[0];
self.a.f[13] = b[1];
self.a.f[14] = b[2];
self.a.f[15] = b[3];
for i in 0..16 {
cb[i] = self.a.f[i]
}
self.a.ecb_encrypt(&mut cb);
for i in 0..16 {
if j >= len {
break;
}
cipher[j] = plain[j] ^ cb[i];
self.statex[i] ^= cipher[j];
j += 1;
self.lenc[1] += 1;
if self.lenc[1] == 0 {
self.lenc[0] += 1
}
}
self.gf2mul()
}
if len % 16 != 0 {
self.status = GCM_NOT_ACCEPTING_MORE
}
true
}
pub fn add_cipher(&mut self, plain: &mut [u8], cipher: &[u8], len: usize) -> bool {
let mut cb: [u8; 16] = [0; 16];
let mut b: [u8; 4] = [0; 4];
let mut counter: u32;
if self.status == GCM_ACCEPTING_HEADER {
self.status = GCM_ACCEPTING_CIPHER
}
if self.status != GCM_ACCEPTING_CIPHER {
return false;
}
let mut j = 0;
while j < len {
b[0] = self.a.f[12];
b[1] = self.a.f[13];
b[2] = self.a.f[14];
b[3] = self.a.f[15];
counter = GCM::pack(b);
counter += 1;
b = GCM::unpack(counter);
self.a.f[12] = b[0];
self.a.f[13] = b[1];
self.a.f[14] = b[2];
self.a.f[15] = b[3];
for i in 0..16 {
cb[i] = self.a.f[i]
}
self.a.ecb_encrypt(&mut cb);
for i in 0..16 {
if j >= len {
break;
}
let oc = cipher[j];
plain[j] = cipher[j] ^ cb[i];
self.statex[i] ^= oc;
j += 1;
self.lenc[1] += 1;
if self.lenc[1] == 0 {
self.lenc[0] += 1
}
}
self.gf2mul()
}
if len % 16 != 0 {
self.status = GCM_NOT_ACCEPTING_MORE
}
true
}
pub fn finish(&mut self,tag: &mut [u8], extract: bool) {
self.wrap();
if extract {
self.a.ecb_encrypt(&mut (self.y_0));
for i in 0..16 {
self.y_0[i] ^= self.statex[i]
}
for i in 0..16 {
tag[i] = self.y_0[i];
self.y_0[i] = 0;
self.statex[i] = 0
}
}
self.status = GCM_FINISHED;
self.a.end();
}
pub fn hex2bytes(hex: &[u8], bin: &mut [u8]) {
let len = hex.len();
for i in 0..len / 2 {
let mut v: u8;
let mut c = hex[2 * i];
if c >= b'0' && c <= b'9' {
v = c - b'0';
} else if c >= b'A' && c <= b'F' {
v = c - b'A' + 10;
} else if c >= b'a' && c <= b'f' {
v = c - b'a' + 10;
} else {
v = 0;
}
v <<= 4;
c = hex[2 * i + 1];
if c >= b'0' && c <= b'9' {
v += c - b'0';
} else if c >= b'A' && c <= b'F' {
v += c - b'A' + 10;
} else if c >= b'a' && c <= b'f' {
v += c - b'a' + 10;
} else {
v = 0;
}
bin[i] = v;
}
}
}
pub fn encrypt(c: &mut [u8],t: &mut [u8],k: &[u8],iv: &[u8],h: &[u8],p: &[u8]) {
let mut g=GCM::new();
g.init(k.len(),k,iv.len(),iv);
g.add_header(h,h.len());
g.add_plain(c,p,p.len());
g.finish(t,true)
}
pub fn decrypt(p: &mut [u8],t: &mut [u8],k: &[u8],iv: &[u8],h: &[u8],c: &[u8]) {
let mut g=GCM::new();
g.init(k.len(),k,iv.len(),iv);
g.add_header(h,h.len());
g.add_cipher(p,c,c.len());
g.finish(t,true);
}