github.com/iotexproject/iotex-core@v1.14.1-rc1/crypto/README.md (about) 1 ## CGO Binding Guide 2 ### Software Requirement 3 * CMAKE (Version at least 3.10.2) 4 * Install on MacOS: 5 ``` 6 brew install cmake 7 ``` 8 * Install on Linux: 9 ``` 10 wget http://www.cmake.org/files/v3.11/cmake-3.11.0-Linux-x86_64.tar.gz 11 tar -xvf cmake-3.11.0-Linux-x86_64.tar.gz 12 cd cmake-3.11.0-Linux-x86_64 13 sudo cp -r bin /usr/ 14 sudo cp -r share /usr/ 15 sudo cp -r doc /usr/share/ 16 sudo cp -r man /usr/share/ 17 ``` 18 19 20 21 ### CGO Development Guide 22 ##### 1. Declare the use of CGO and files binding 23 24 ``` Go 25 //#include "lib/ecckey.h" 26 //#include "lib/ecckey.c" 27 import “C” 28 ``` 29 * Just use import “C” to start using CGO. It comes with Go. No need to install any other library. 30 * “//” is not a regular comment sign in CGO declaration. It starts a directive and will not be ignored by compiler. 31 * import “C” should be immediately preceded by //#include XXX or //#cgo. 32 * //#include XXX indicates the use of c header files and c source code files 33 * //#cgo is used to indicate the use of flags like CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS etc. to tweak the behavior of C, C++ compiler. The //#cgo directive can also include a list of build constraints that limit the os and architecture. It can also indicate the C library, so that you do not have to include the source code files. 34 ``` Go 35 //#cgo darwin LDFLAGS: -L${SRCDIR}/lib -lsect283k1_macos 36 //#cgo linux LDFLAGS: -L${SRCDIR}/lib -lsect283k1_ubuntu 37 ``` 38 This indicates the library sect283k1_macos will be used in Mac platform, 39 sect283k1_ubuntu in Linux platform. 40 Both of them are located in directory ${SRCDIR}/lib. 41 -L specifies the location of the library. 42 ``` Go 43 package crypto 44 45 //#include "lib/ecckey.h" 46 //#include "lib/ecdsa.h" 47 //#include "lib/sect283k1.h" 48 //#cgo darwin LDFLAGS: -L${SRCDIR}/lib -lsect283k1_macos 49 //#cgo linux LDFLAGS: -L${SRCDIR}/lib -lsect283k1_ubuntu 50 import "C" 51 import ( 52 "bytes" 53 "encoding/binary" 54 "errors" 55 56 "github.com/iotexproject/iotex-core/pkg/enc" 57 "github.com/iotexproject/go-pkgs/crypto" 58 ) 59 ``` 60 61 ##### 2. Go references C 62 * Go calls C functions: Just call C.function_name_in_C(…) 63 * Go uses any type in C: C.type_in_C, like C.uint32_t, C.ec_point_aff_twist 64 * Pointer pass in CGO is the same as in go 65 * Go can access any fields of C struct in the same way in Go struct 66 ``` Go 67 // C header file 68 69 typedef struct 70 { 71 uint32_t d[9]; 72 ec283_point_lambda_aff Q; 73 }ec283_key_pair; 74 75 void keypair_generation(ec283_key_pair *key); 76 77 // Go file 78 func (c *ec283) NewKeyPair() (keypair.PublicKey, keypair.PrivateKey, error) { 79 var kp C.ec283_key_pair 80 C.keypair_generation(&kp) 81 pubKey := kp.Q 82 privKey := kp.d 83 ... 84 } 85 ``` 86 * Primitive type conversion: Just cast the type from C type to Go or from Go to C 87 ``` Go 88 // privkey has go type elements, privkeySer has C type elements 89 for i := 0; i < privkeySize; i++ { 90 privkeySer[i] = (C.uint32_t)(privkey[i]) 91 } 92 93 // sigSer has C type elements, sig has go type elements 94 for i, x := range sigSer { 95 sig[i] = (uint32)(x) 96 } 97 ``` 98 ##### 3. Go passes an array to C 99 100 Just pass the first element of the slice as a pointer, and cast the pointer to C corresponding 101 type. The slice in Go is a struct including header and elements, so &slice won’t give you the 102 first element. 103 ``` Go 104 // C file 105 // d and sig are arrays 106 107 uint32_t BLS_sign(uint32_t *d, const uint8_t *msg, uint64_t mlen, uint32_t *sig); 108 109 // Go file 110 // Sign signs a message given a private key 111 112 func (b *bls) Sign(privkey []uint32, msg []byte) (bool, []byte, error) { 113 var sigSer [sigSize]C.uint32_t 114 msgString := string(msg[:]) 115 116 if ok := C.BLS_sign((*C.uint32_t)(unsafe.Pointer(&privkey[0])), (*C.uint8_t)(&msg[0]), (C.uint64_t)(len(msgString)), &sigSer[0]); ok == 1 { 117 sig, err := b.signatureSerialization(sigSer) 118 if err != nil { 119 return false, []byte{}, err 120 } 121 return true, sig, nil 122 } 123 return false, []byte{}, ErrSignError 124 } 125 126 ``` 127 ##### 4. Use struct defined in C 128 * Construct a corresponding struct in Go that mimics the same struct in C. 129 * (Recommended) Write function to serialize and deserialize the struct, so the information can be exposed outside independently. 130 ``` Go 131 // C header file and C struct definition 132 typedef struct 133 { 134 uint32_t x[9]; 135 uint32_t l[9]; 136 }ec283_point_lambda_aff; 137 138 // Go file 139 140 func (*ec283) publicKeySerialization(pubKey C.ec283_point_lambda_aff) (keypair.PublicKey, error) { 141 var xl [18]uint32 142 for i := 0; i < 9; i++ { 143 xl[i] = (uint32)(pubKey.x[i]) 144 xl[i+9] = (uint32)(pubKey.l[i]) 145 } 146 buf := new(bytes.Buffer) 147 err := binary.Write(buf, enc.MachineEndian, xl) 148 if err != nil { 149 return keypair.ZeroPublicKey, err 150 } 151 return keypair.BytesToPublicKey(buf.Bytes()) 152 } 153 154 func (*ec283) publicKeyDeserialization(pubKey keypair.PublicKey) (C.ec283_point_lambda_aff, error) { 155 var xl [18]uint32 156 var pub C.ec283_point_lambda_aff 157 rbuf := bytes.NewReader(pubKey[:]) 158 err := binary.Read(rbuf, enc.MachineEndian, &xl) 159 if err != nil { 160 return pub, err 161 } 162 for i := 0; i < 9; i++ { 163 pub.x[i] = (C.uint32_t)(xl[i]) 164 pub.l[i] = (C.uint32_t)(xl[i+9]) 165 } 166 return pub, nil 167 } 168 ``` 169