// crypto.go package android import ( "bytes" "compress/zlib" "crypto/aes" "crypto/cipher" "crypto/elliptic" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "golang.org/x/crypto/hkdf" "hash" "io" "xiawan/wx/clientsdk/android/mmproto" proto "github.com/golang/protobuf/proto" ) type HYBRID_STATUS int32 type HybridEcdhClient struct { hybridStatus HYBRID_STATUS clientHash hash.Hash serverHash hash.Hash clientStaticPub []byte clientEcdsaPub []byte genClientPub []byte genClientPriv []byte curve elliptic.Curve } func AesGcmEncrypt(key, nonce, input, additional []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { //fmt.Println("cipher init faile....") return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { fmt.Println("gcm init faile....") return nil, err } result := aesgcm.Seal(nil, nonce, input, additional) return result, nil } func AesGcmDecrypt(key, nonce, input, additional []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { fmt.Println("cipher init faile....") return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { fmt.Println("gcm init faile....") return nil, err } return aesgcm.Open(nil, nonce, input, additional) } func AesGcmEncryptWithCompress(key, nonce, input, additional []byte) ([]byte, error) { var b bytes.Buffer w := zlib.NewWriter(&b) w.Write(input) w.Close() data, _ := AesGcmEncrypt(key, nonce, b.Bytes(), additional) encData := data[:len(data)-16] tag := data[len(data)-16:] totalData := []byte{} totalData = append(totalData, encData...) totalData = append(totalData, nonce...) totalData = append(totalData, tag...) return totalData, nil } func AesGcmDecryptWithUnCompress(key, input, additional []byte) ([]byte, error) { inputSize := len(input) nonce := make([]byte, 12) copy(nonce, input[inputSize-28:inputSize-16]) tag := make([]byte, 16) copy(tag, input[inputSize-16:]) cipherText := make([]byte, inputSize-28) copy(cipherText, input[:inputSize-28]) cipherText = append(cipherText, tag...) result, _ := AesGcmDecrypt(key, nonce, cipherText, additional) b := bytes.NewReader(result) var out bytes.Buffer r, _ := zlib.NewReader(b) io.Copy(&out, r) r.Close() return out.Bytes(), nil } func Ecdh(curve elliptic.Curve, pub, priv []byte) []byte { x, y := elliptic.Unmarshal(curve, pub) if x == nil { return nil } xShared, _ := curve.ScalarMult(x, y, priv) sharedKey := make([]byte, (curve.Params().BitSize+7)>>3) xBytes := xShared.Bytes() copy(sharedKey[len(sharedKey)-len(xBytes):], xBytes) dh := sha256.Sum256(sharedKey) return dh[:] } func (h *HybridEcdhClient) Init() { h.hybridStatus = HYBRID_ENC h.clientHash = sha256.New() h.serverHash = sha256.New() h.clientStaticPub, _ = hex.DecodeString("0495BC6E5C1331AD172D0F35B1792C3CE63F91572ABD2DD6DF6DAC2D70195C3F6627CCA60307305D8495A8C38B4416C75021E823B6C97DFFE79C14CB7C3AF8A586") h.clientEcdsaPub, _ = hex.DecodeString("2D2D2D2D2D424547494E205055424C4943204B45592D2D2D2D2D0A4D466B77457759484B6F5A497A6A3043415159494B6F5A497A6A3044415163445167414552497979694B33533950374854614B4C654750314B7A6243435139490A4C537845477861465645346A6E5A653646717777304A6877356D41716266574C4B364E69387075765356364371432B44324B65533373767059773D3D0A2D2D2D2D2D454E44205055424C4943204B45592D2D2D2D2D0A") h.curve = elliptic.P256() } func (h *HybridEcdhClient) Encrypt(input []byte) []byte { if h.hybridStatus != HYBRID_ENC { return nil } priv, x, y, error := elliptic.GenerateKey(h.curve, rand.Reader) if error != nil { return nil } h.genClientPriv = priv h.genClientPub = elliptic.Marshal(h.curve, x, y) ecdhKey := Ecdh(h.curve, h.clientStaticPub, h.genClientPriv) //hash1 h1 := sha256.New() h1.Write([]byte("1")) h1.Write([]byte("415")) h1.Write(h.genClientPub) h1Sum := h1.Sum(nil) //Random random := make([]byte, 32) io.ReadFull(rand.Reader, random) nonce1 := make([]byte, 12) io.ReadFull(rand.Reader, nonce1) gcm1, _ := AesGcmEncryptWithCompress(ecdhKey[0:0x18], nonce1, random, h1Sum) //hkdf salt, _ := hex.DecodeString("73656375726974792068646B6620657870616E64") hkdfKey := make([]byte, 56) hkdf.New(sha256.New, random, salt, h1Sum).Read(hkdfKey) //hash2 h2 := sha256.New() h2.Write([]byte("1")) h2.Write([]byte("415")) h2.Write(h.genClientPub) h2.Write(gcm1) h2Sum := h2.Sum(nil) nonce2 := make([]byte, 12) io.ReadFull(rand.Reader, nonce2) gcm2, _ := AesGcmEncryptWithCompress(hkdfKey[0:0x18], nonce2, input, h2Sum) var nid int32 = 415 secKey := &mmproto.SecKey{ Nid: &nid, Key: h.genClientPub, } var ver int32 = 1 he := &mmproto.HybridEcdhReq{ Version: &ver, SecKey: secKey, Gcm1: gcm1, Autokey: []byte{}, Gcm2: gcm2, } protoMsg, _ := proto.Marshal(he) // update client h.clientHash.Write(hkdfKey[0x18:0x38]) h.clientHash.Write(input) // update server h.serverHash.Write(gcm2) h.hybridStatus = HYBRID_DEC return protoMsg } func (h *HybridEcdhClient) Decrypt(input []byte) []byte { if h.hybridStatus != HYBRID_DEC { return nil } var resp mmproto.HybridEcdhResp proto.Unmarshal(input, &resp) h.serverHash.Write(resp.GetGcm1()) // hServ := h.serverHash.Sum(nil) // fmt.Printf("%x\n", hServ) // ecdsa.Verify(h.clientEcdsaPub, resp.GetGcm2(), hServ) ecKey := Ecdh(h.curve, resp.GetSecKey().GetKey(), h.genClientPriv) h.clientHash.Write([]byte("415")) h.clientHash.Write(resp.GetSecKey().GetKey()) h.clientHash.Write([]byte("1")) hCli := h.clientHash.Sum(nil) plain, _ := AesGcmDecryptWithUnCompress(ecKey[0:0x18], resp.GetGcm1(), hCli) return plain }