/* * 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