sign.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package signer
  2. import (
  3. "archive/zip"
  4. "bytes"
  5. "crypto/sha256"
  6. "encoding/base64"
  7. "io"
  8. "path"
  9. "strconv"
  10. "time"
  11. "github.com/langgenius/dify-plugin-daemon/internal/core/license/private_key"
  12. "github.com/langgenius/dify-plugin-daemon/internal/core/plugin_packager/decoder"
  13. "github.com/langgenius/dify-plugin-daemon/internal/utils/encryption"
  14. "github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
  15. )
  16. /*
  17. DifyPlugin is a file type that represents a plugin, it's designed to based on zip file format.
  18. When signing a plugin, we use RSA-4096 to create a signature for the plugin and write the signature
  19. into comment field of the zip file.
  20. */
  21. // SignPlugin is a function that signs a plugin
  22. // It takes a plugin as a stream of bytes and signs it with RSA-4096
  23. func SignPlugin(plugin []byte) ([]byte, error) {
  24. // load private key
  25. private_key, err := encryption.LoadPrivateKey(private_key.PRIVATE_KEY)
  26. if err != nil {
  27. return nil, err
  28. }
  29. decoder, err := decoder.NewZipPluginDecoder(plugin)
  30. if err != nil {
  31. return nil, err
  32. }
  33. // create a new zip writer
  34. zip_buffer := new(bytes.Buffer)
  35. zip_writer := zip.NewWriter(zip_buffer)
  36. defer zip_writer.Close()
  37. // store temporary hash
  38. data := new(bytes.Buffer)
  39. // read one by one
  40. err = decoder.Walk(func(filename, dir string) error {
  41. file, err := decoder.ReadFile(path.Join(dir, filename))
  42. if err != nil {
  43. return err
  44. }
  45. // calculate sha256 hash of the file
  46. hash := sha256.New()
  47. hash.Write(file)
  48. hashed := hash.Sum(nil)
  49. // write the hash into data
  50. data.Write(hashed)
  51. // create a new file in the zip writer
  52. file_writer, err := zip_writer.Create(path.Join(dir, filename))
  53. if err != nil {
  54. return err
  55. }
  56. _, err = io.Copy(file_writer, bytes.NewReader(file))
  57. if err != nil {
  58. return err
  59. }
  60. return nil
  61. })
  62. if err != nil {
  63. return nil, err
  64. }
  65. // get current time
  66. ct := time.Now().Unix()
  67. // convert time to bytes
  68. time_string := strconv.FormatInt(ct, 10)
  69. // write the time into data
  70. data.Write([]byte(time_string))
  71. // sign the data
  72. signature, err := encryption.RSASign(private_key, data.Bytes())
  73. if err != nil {
  74. return nil, err
  75. }
  76. // write the signature into the comment field of the zip file
  77. comments := parser.MarshalJson(map[string]any{
  78. "signature": base64.StdEncoding.EncodeToString(signature),
  79. "time": ct,
  80. })
  81. // write signature
  82. err = zip_writer.SetComment(comments)
  83. if err != nil {
  84. return nil, err
  85. }
  86. // close the zip writer
  87. err = zip_writer.Close()
  88. if err != nil {
  89. return nil, err
  90. }
  91. return zip_buffer.Bytes(), nil
  92. }