ceremonyclient/crates/bls48581/src/gcm.rs
Cassandra Heart 35561a9e41
v1.4.19-p1 (#235)
* v1.4.19-p1

* remove integrity check from startup
2024-06-15 11:05:06 -05:00

509 lines
14 KiB
Rust

/*
* Copyright (c) 2012-2020 MIRACL UK Ltd.
*
* This file is part of MIRACL Core
* (see https://github.com/miracl/core).
*
* Licensed 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 */
((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] {
/* unpack bytes from a word */
[
((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
} /* 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).wrapping_add(1); // + 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
}
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
}
true
}
/* Add Plaintext - included and encrypted */
/* if plain is None - encrypts cipher in place */
pub fn add_plain(&mut self, cipher: &mut [u8], plain: Option<&[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;
}
if let Some(sp) = plain {
cipher[j] = sp[j] ^ cb[i];
} else {
cipher[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
}
/* Add Ciphertext - decrypts to plaintext */
/* if cipher is None - decrypts plain in place */
pub fn add_cipher(&mut self, plain: &mut [u8], cipher: Option<&[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:u8;
if let Some(sc) = cipher {
oc = sc[j];
} else {
oc = plain[j];
}
plain[j] = oc ^ 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
}
/* Finish and extract Tag */
pub fn finish(&mut self,tag: &mut [u8], extract: bool) {
/* Finish off GHASH and extract tag (MAC) */
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();
}
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,Some(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,Some(c),c.len());
g.finish(t,true);
}
/*
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!("");
}
*/