ceremonyclient/ec/bls48581/core/GCM.go
2023-04-15 00:05:26 -04:00

458 lines
9.5 KiB
Go

/*
* 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.
*/
/*
* Implementation of the AES-GCM Encryption/Authentication
*
* Some restrictions..
* 1. Only for use with AES
* 2. Returned tag is always 128-bits. Truncate at your own risk.
* 3. The order of function calls must follow some rules
*
* Typical sequence of calls..
* 1. call GCM_init
* 2. call GCM_add_header any number of times, as long as length of header is multiple of 16 bytes (block size)
* 3. call GCM_add_header one last time with any length of header
* 4. call GCM_add_cipher any number of times, as long as length of cipher/plaintext is multiple of 16 bytes
* 5. call GCM_add_cipher one last time with any length of cipher/plaintext
* 6. call GCM_finish to extract the tag.
*
* See http://www.mindspring.com/~dmcgrew/gcm-nist-6.pdf
*/
package core
import (
// "fmt"
"strconv"
)
const gcm_NB int = 4
const GCM_ACCEPTING_HEADER int = 0
const GCM_ACCEPTING_CIPHER int = 1
const GCM_NOT_ACCEPTING_MORE int = 2
const GCM_FINISHED int = 3
const GCM_ENCRYPTING int = 0
const GCM_DECRYPTING int = 1
type GCM struct {
table [128][4]uint32 /* 2k bytes */
stateX [16]byte
Y_0 [16]byte
counter int
lenA [2]uint32
lenC [2]uint32
status int
a *AES
}
func gcm_pack(b [4]byte) uint32 { /* pack bytes into a 32-bit Word */
return ((uint32(b[0]) & 0xff) << 24) | ((uint32(b[1]) & 0xff) << 16) | ((uint32(b[2]) & 0xff) << 8) | (uint32(b[3]) & 0xff)
}
func gcm_unpack(a uint32) [4]byte { /* unpack bytes from a word */
var b = [4]byte{byte((a >> 24) & 0xff), byte((a >> 16) & 0xff), byte((a >> 8) & 0xff), byte(a & 0xff)}
return b
}
func (G *GCM) precompute(H []byte) {
var b [4]byte
j := 0
for i := 0; i < gcm_NB; i++ {
b[0] = H[j]
b[1] = H[j+1]
b[2] = H[j+2]
b[3] = H[j+3]
G.table[0][i] = gcm_pack(b)
j += 4
}
for i := 1; i < 128; i++ {
c := uint32(0)
for j := 0; j < gcm_NB; j++ {
G.table[i][j] = c | (G.table[i-1][j])>>1
c = G.table[i-1][j] << 31
}
if c != 0 {
G.table[i][0] ^= 0xE1000000
} /* irreducible polynomial */
}
}
func (G *GCM) gf2mul() { /* gf2m mul - Z=H*X mod 2^128 */
var P [4]uint32
for i := 0; i < 4; i++ {
P[i] = 0
}
j := uint(8)
m := 0
for i := 0; i < 128; i++ {
j--
c := uint32((G.stateX[m] >> j) & 1)
c = ^c + 1
for k := 0; k < gcm_NB; k++ {
P[k] ^= (G.table[i][k] & c)
}
if j == 0 {
j = 8
m++
if m == 16 {
break
}
}
}
j = 0
for i := 0; i < gcm_NB; i++ {
b := gcm_unpack(P[i])
G.stateX[j] = b[0]
G.stateX[j+1] = b[1]
G.stateX[j+2] = b[2]
G.stateX[j+3] = b[3]
j += 4
}
}
func (G *GCM) wrap() { /* Finish off GHASH */
var F [4]uint32
var L [16]byte
/* convert lengths from bytes to bits */
F[0] = (G.lenA[0] << 3) | (G.lenA[1]&0xE0000000)>>29
F[1] = G.lenA[1] << 3
F[2] = (G.lenC[0] << 3) | (G.lenC[1]&0xE0000000)>>29
F[3] = G.lenC[1] << 3
j := 0
for i := 0; i < gcm_NB; i++ {
b := gcm_unpack(F[i])
L[j] = b[0]
L[j+1] = b[1]
L[j+2] = b[2]
L[j+3] = b[3]
j += 4
}
for i := 0; i < 16; i++ {
G.stateX[i] ^= L[i]
}
G.gf2mul()
}
func (G *GCM) ghash(plain []byte, len int) bool {
if G.status == GCM_ACCEPTING_HEADER {
G.status = GCM_ACCEPTING_CIPHER
}
if G.status != GCM_ACCEPTING_CIPHER {
return false
}
j := 0
for j < len {
for i := 0; i < 16 && j < len; i++ {
G.stateX[i] ^= plain[j]
j++
G.lenC[1]++
if G.lenC[1] == 0 {
G.lenC[0]++
}
}
G.gf2mul()
}
if len%16 != 0 {
G.status = GCM_NOT_ACCEPTING_MORE
}
return true
}
/* Initialize GCM mode */
func (G *GCM) Init(nk int, key []byte, niv int, iv []byte) { /* iv size niv is usually 12 bytes (96 bits). AES key size nk can be 16,24 or 32 bytes */
var H [16]byte
for i := 0; i < 16; i++ {
H[i] = 0
G.stateX[i] = 0
}
G.a = new(AES)
G.a.Init(AES_ECB, nk, key, iv)
G.a.ecb_encrypt(H[:]) /* E(K,0) */
G.precompute(H[:])
G.lenA[0] = 0
G.lenC[0] = 0
G.lenA[1] = 0
G.lenC[1] = 0
if niv == 12 {
for i := 0; i < 12; i++ {
G.a.f[i] = iv[i]
}
b := gcm_unpack(uint32(1))
G.a.f[12] = b[0]
G.a.f[13] = b[1]
G.a.f[14] = b[2]
G.a.f[15] = b[3] /* initialise IV */
for i := 0; i < 16; i++ {
G.Y_0[i] = G.a.f[i]
}
} else {
G.status = GCM_ACCEPTING_CIPHER
G.ghash(iv, niv) /* GHASH(H,0,IV) */
G.wrap()
for i := 0; i < 16; i++ {
G.a.f[i] = G.stateX[i]
G.Y_0[i] = G.a.f[i]
G.stateX[i] = 0
}
G.lenA[0] = 0
G.lenC[0] = 0
G.lenA[1] = 0
G.lenC[1] = 0
}
G.status = GCM_ACCEPTING_HEADER
}
/* Add Header data - included but not encrypted */
func (G *GCM) Add_header(header []byte, len int) bool { /* Add some header. Won't be encrypted, but will be authenticated. len is length of header */
if G.status != GCM_ACCEPTING_HEADER {
return false
}
j := 0
for j < len {
for i := 0; i < 16 && j < len; i++ {
G.stateX[i] ^= header[j]
j++
G.lenA[1]++
if G.lenA[1] == 0 {
G.lenA[0]++
}
}
G.gf2mul()
}
if len%16 != 0 {
G.status = GCM_ACCEPTING_CIPHER
}
return true
}
/* Add Plaintext - included and encrypted */
func (G *GCM) Add_plain(plain []byte, len int) []byte {
var B [16]byte
var b [4]byte
cipher := make([]byte, len)
var counter uint32 = 0
if G.status == GCM_ACCEPTING_HEADER {
G.status = GCM_ACCEPTING_CIPHER
}
if G.status != GCM_ACCEPTING_CIPHER {
return nil
}
j := 0
for j < len {
b[0] = G.a.f[12]
b[1] = G.a.f[13]
b[2] = G.a.f[14]
b[3] = G.a.f[15]
counter = gcm_pack(b)
counter++
b = gcm_unpack(counter)
G.a.f[12] = b[0]
G.a.f[13] = b[1]
G.a.f[14] = b[2]
G.a.f[15] = b[3] /* increment counter */
for i := 0; i < 16; i++ {
B[i] = G.a.f[i]
}
G.a.ecb_encrypt(B[:]) /* encrypt it */
for i := 0; i < 16 && j < len; i++ {
cipher[j] = (plain[j] ^ B[i])
G.stateX[i] ^= cipher[j]
j++
G.lenC[1]++
if G.lenC[1] == 0 {
G.lenC[0]++
}
}
G.gf2mul()
}
if len%16 != 0 {
G.status = GCM_NOT_ACCEPTING_MORE
}
return cipher
}
/* Add Ciphertext - decrypts to plaintext */
func (G *GCM) Add_cipher(cipher []byte, len int) []byte {
var B [16]byte
var b [4]byte
plain := make([]byte, len)
var counter uint32 = 0
if G.status == GCM_ACCEPTING_HEADER {
G.status = GCM_ACCEPTING_CIPHER
}
if G.status != GCM_ACCEPTING_CIPHER {
return nil
}
j := 0
for j < len {
b[0] = G.a.f[12]
b[1] = G.a.f[13]
b[2] = G.a.f[14]
b[3] = G.a.f[15]
counter = gcm_pack(b)
counter++
b = gcm_unpack(counter)
G.a.f[12] = b[0]
G.a.f[13] = b[1]
G.a.f[14] = b[2]
G.a.f[15] = b[3] /* increment counter */
for i := 0; i < 16; i++ {
B[i] = G.a.f[i]
}
G.a.ecb_encrypt(B[:]) /* encrypt it */
for i := 0; i < 16 && j < len; i++ {
oc := cipher[j]
plain[j] = (cipher[j] ^ B[i])
G.stateX[i] ^= oc
j++
G.lenC[1]++
if G.lenC[1] == 0 {
G.lenC[0]++
}
}
G.gf2mul()
}
if len%16 != 0 {
G.status = GCM_NOT_ACCEPTING_MORE
}
return plain
}
/* Finish and extract Tag */
func (G *GCM) Finish(extract bool) []byte { /* Finish off GHASH and extract tag (MAC) */
var tag []byte
G.wrap()
/* extract tag */
if extract {
G.a.ecb_encrypt(G.Y_0[:]) /* E(K,Y0) */
for i := 0; i < 16; i++ {
G.Y_0[i] ^= G.stateX[i]
}
for i := 0; i < 16; i++ {
tag = append(tag, G.Y_0[i])
G.Y_0[i] = 0
G.stateX[i] = 0
}
}
G.status = GCM_FINISHED
G.a.End()
return tag
}
func hex2bytes(s string) []byte {
lgh := len(s)
data := make([]byte, lgh/2)
for i := 0; i < lgh; i += 2 {
a, _ := strconv.ParseInt(s[i:i+2], 16, 32)
data[i/2] = byte(a)
}
return data
}
func GCM_ENCRYPT(K []byte, IV []byte, H []byte, P []byte) ([]byte, []byte) {
g := new(GCM)
g.Init(len(K), K, len(IV), IV)
g.Add_header(H, len(H))
C := g.Add_plain(P, len(P))
T := g.Finish(true)
return C, T
}
func GCM_DECRYPT(K []byte, IV []byte, H []byte, C []byte) ([]byte, []byte) {
g := new(GCM)
g.Init(len(K), K, len(IV), IV)
g.Add_header(H, len(H))
P := g.Add_cipher(C, len(C))
T := g.Finish(true)
return P, T
}
/*
func main() {
KT:="feffe9928665731c6d6a8f9467308308"
MT:="d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"
HT:="feedfacedeadbeeffeedfacedeadbeefabaddad2"
NT:="9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b";
// Tag should be 619cc5aefffe0bfa462af43c1699d050
g:=new(GCM)
M:=hex2bytes(MT)
H:=hex2bytes(HT)
N:=hex2bytes(NT)
K:=hex2bytes(KT)
lenM:=len(M)
lenH:=len(H)
lenK:=len(K)
lenIV:=len(N)
fmt.Printf("Plaintext=\n");
for i:=0;i<lenM;i++ {fmt.Printf("%02x",M[i])}
fmt.Printf("\n")
g.Init(lenK,K,lenIV,N)
g.Add_header(H,lenH)
C:=g.Add_plain(M,lenM)
T:=g.Finish(true)
fmt.Printf("Ciphertext=\n")
for i:=0;i<lenM;i++ {fmt.Printf("%02x",C[i])}
fmt.Printf("\n")
fmt.Printf("Tag=\n")
for i:=0;i<16;i++ {fmt.Printf("%02x",T[i])}
fmt.Printf("\n")
g.Init(lenK,K,lenIV,N)
g.Add_header(H,lenH)
P:=g.Add_cipher(C,lenM)
T=g.Finish(true)
fmt.Printf("Plaintext=\n");
for i:=0;i<lenM;i++ {fmt.Printf("%02x",P[i])}
fmt.Printf("\n")
fmt.Printf("Tag=\n");
for i:=0;i<16;i++ {fmt.Printf("%02x",T[i])}
fmt.Printf("\n")
}
*/