| /* |
| 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. |
| */ |
| |
| /* |
| * 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 amcl |
| |
| 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) [16]byte { /* Finish off GHASH and extract tag (MAC) */ |
| var tag [16]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[i]=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 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") |
| } |
| */ |