| /* |
| 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 := (G.stateX[m] >> j) & 1 |
| if c != 0 { |
| for k := 0; k < gcm_NB; k++ { |
| P[k] ^= G.table[i][k] |
| } |
| } |
| 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(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, 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++ { |
| plain[j] = (cipher[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 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 |
| } |
| |
| /* AES-GCM Encryption: |
| K is key, H is header, IV is initialization vector and P is plaintext. |
| Returns cipthertext and tag (MAC) */ |
| func AES_GCM_ENCRYPT(K, IV, H, P []byte) ([]byte, []byte) { |
| g := new(GCM) |
| lenIV := len(IV) |
| lenH := len(H) |
| lenP := len(P) |
| |
| g.Init(K, lenIV, IV) |
| g.Add_header(H, lenH) |
| C := g.Add_plain(P, lenP) |
| T := g.Finish(true) |
| return C, T[:] |
| } |
| |
| /* AES-GCM Deryption: |
| K is key, H is header, IV is initialization vector and P is plaintext. |
| Returns cipthertext and tag (MAC) */ |
| func AES_GCM_DECRYPT(K, IV, H, C []byte) ([]byte, []byte) { |
| g := new(GCM) |
| lenIV := len(IV) |
| lenH := len(H) |
| lenC := len(C) |
| |
| g.Init(K, lenIV, IV) |
| g.Add_header(H, lenH) |
| P := g.Add_cipher(C, lenC) |
| T := g.Finish(true) |
| return P, T[:] |
| } |
| |
| /* |
| 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(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(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") |
| } |
| */ |