| /* |
| 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 aes; |
| use 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!(""); |
| |
| } |
| */ |