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