| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| 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; |
| const GCM_ENCRYPTING: usize = 0; |
| const GCM_DECRYPTING: usize = 1; |
| |
| use crate::aes; |
| use crate::aes::AES; |
| |
| pub struct GCM { |
| table: [[u32; 4]; 128], |
| statex: [u8; 16], |
| y_0: [u8; 16], |
| // counter: usize, |
| lena: [u32; 2], |
| lenc: [u32; 2], |
| status: usize, |
| a: AES, |
| } |
| |
| impl GCM { |
| fn pack(b: [u8; 4]) -> u32 { |
| /* pack bytes into a 32-bit Word */ |
| return ((((b[0]) & 0xff) as u32) << 24) |
| | ((((b[1]) & 0xff) as u32) << 16) |
| | ((((b[2]) & 0xff) as u32) << 8) |
| | (((b[3]) & 0xff) as u32); |
| } |
| |
| fn unpack(a: u32) -> [u8; 4] { |
| /* unpack bytes from a word */ |
| let b: [u8; 4] = [ |
| ((a >> 24) & 0xff) as u8, |
| ((a >> 16) & 0xff) as u8, |
| ((a >> 8) & 0xff) as u8, |
| (a & 0xff) as u8, |
| ]; |
| return b; |
| } |
| |
| 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 |
| } /* irreducible polynomial */ |
| } |
| } |
| |
| fn gf2mul(&mut self) { |
| /* gf2m mul - Z=H*X mod 2^128 */ |
| 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) + 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) { |
| /* Finish off GHASH */ |
| let mut f: [u32; 4] = [0; 4]; |
| let mut el: [u8; 16] = [0; 16]; |
| |
| /* convert lengths from bytes to bits */ |
| 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 |
| } |
| return true; |
| } |
| |
| /* Initialize GCM mode */ |
| pub fn init(&mut self, nk: usize, key: &[u8], niv: usize, iv: &[u8]) { |
| /* iv size niv is usually 12 bytes (96 bits). AES key size nk can be 16,24 or 32 bytes */ |
| 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); /* E(K,0) */ |
| 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]; /* initialise IV */ |
| for i in 0..16 { |
| self.y_0[i] = self.a.f[i] |
| } |
| } else { |
| self.status = GCM_ACCEPTING_CIPHER; |
| self.ghash(iv, niv); /* GHASH(H,0,IV) */ |
| 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], |
| //counter:0, |
| lena: [0; 2], |
| lenc: [0; 2], |
| status: 0, |
| a: AES::new(), |
| } |
| } |
| |
| /* Add Header data - included but not encrypted */ |
| pub fn add_header(&mut self, header: &[u8], len: usize) -> bool { |
| /* Add some header. Won't be encrypted, but will be authenticated. len is length of header */ |
| 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 |
| } |
| return true; |
| } |
| |
| /* Add Plaintext - included and encrypted */ |
| 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]; /* increment counter */ |
| for i in 0..16 { |
| cb[i] = self.a.f[i] |
| } |
| self.a.ecb_encrypt(&mut cb); /* encrypt it */ |
| |
| 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 |
| } |
| return true; |
| } |
| |
| /* Add Ciphertext - decrypts to plaintext */ |
| 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]; /* increment counter */ |
| for i in 0..16 { |
| cb[i] = self.a.f[i] |
| } |
| self.a.ecb_encrypt(&mut cb); /* encrypt it */ |
| 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 |
| } |
| return true; |
| } |
| |
| /* Finish and extract Tag */ |
| pub fn finish(&mut self, extract: bool) -> [u8; 16] { |
| /* Finish off GHASH and extract tag (MAC) */ |
| let mut tag: [u8; 16] = [0; 16]; |
| |
| self.wrap(); |
| /* extract tag */ |
| if extract { |
| self.a.ecb_encrypt(&mut (self.y_0)); /* E(K,Y0) */ |
| 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(); |
| return tag; |
| } |
| |
| 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; |
| } |
| } |
| } |
| /* |
| fn main() |
| { |
| let kt=b"feffe9928665731c6d6a8f9467308308"; |
| let mt=b"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"; |
| let ht=b"feedfacedeadbeeffeedfacedeadbeefabaddad2"; |
| let nt=b"9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b"; |
| // Tag should be 619cc5aefffe0bfa462af43c1699d050 |
| |
| let mut gcm=GCM::new(); |
| |
| let len=mt.len()/2; |
| let lenh=ht.len()/2; |
| let lenk=kt.len()/2; |
| let leniv=nt.len()/2; |
| |
| //let mut t:[u8;16]=[0;16]; // Tag |
| let mut k:[u8;16]=[0;16]; // AES Key |
| let mut h:[u8;64]=[0;64]; // Header - to be included in Authentication, but not encrypted |
| let mut n:[u8;100]=[0;100]; // IV - Initialisation vector |
| let mut m:[u8;100]=[0;100]; // Plaintext to be encrypted/authenticated |
| let mut c:[u8;100]=[0;100]; // Ciphertext |
| let mut p:[u8;100]=[0;100]; // Recovered Plaintext |
| |
| GCM::hex2bytes(mt,&mut m); |
| GCM::hex2bytes(ht,&mut h); |
| GCM::hex2bytes(kt,&mut k); |
| GCM::hex2bytes(nt,&mut n); |
| |
| println!("Plaintext="); |
| for i in 0..len {print!("{:02x}",m[i])} |
| println!(""); |
| |
| gcm.init(lenk,&k,leniv,&n); |
| |
| gcm.add_header(&h,lenh); |
| gcm.add_plain(&mut c,&m,len); |
| let mut t=gcm.finish(true); |
| |
| println!("Ciphertext="); |
| for i in 0..len {print!("{:02x}",c[i])} |
| println!(""); |
| |
| println!("Tag="); |
| for i in 0..16 {print!("{:02x}",t[i])} |
| println!(""); |
| |
| gcm.init(lenk,&k,leniv,&n); |
| |
| gcm.add_header(&h,lenh); |
| gcm.add_cipher(&mut p,&c,len); |
| t=gcm.finish(true); |
| |
| println!("Plaintext="); |
| for i in 0..len {print!("{:02x}",p[i])} |
| println!(""); |
| |
| println!("Tag="); |
| for i in 0..16 {print!("{:02x}",t[i])} |
| println!(""); |
| |
| } |
| */ |