package util import ( "bytes" "crypto/ecdsa" "crypto/hmac" "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "crypto/x509" _ "embed" "encoding/base64" "encoding/hex" "encoding/json" "encoding/pem" "errors" "path/filepath" "runtime" "strings" "text/template" "time" "apigo.cc/gojs" "apigo.cc/gojs/goja" "github.com/ZZMarquis/gm/sm3" "github.com/ZZMarquis/gm/sm4" "github.com/ZZMarquis/gm/util" "github.com/obscuren/ecies" "github.com/ssgo/u" "gopkg.in/yaml.v3" ) //go:embed util.ts var utilTS string //go:embed README.md var utilMD string func init() { obj := map[string]any{ "json": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) if r, err := json.Marshal(args.Arguments[0].Export()); err == nil { return vm.ToValue(string(r)) } else { panic(vm.NewGoError(err)) } }, "jsonP": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) if r, err := json.Marshal(args.Arguments[0].Export()); err == nil { r1 := bytes.Buffer{} if err2 := json.Indent(&r1, r, "", " "); err2 == nil { return vm.ToValue(string(r)) } else { return vm.ToValue(string(r)) } } else { panic(vm.NewGoError(err)) } }, "unJson": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) var r any if err := json.Unmarshal(u.Bytes(args.Arguments[0].Export()), &r); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "yaml": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) if r, err := yaml.Marshal(args.Arguments[0].Export()); err == nil { return vm.ToValue(string(r)) } else { panic(vm.NewGoError(err)) } }, "unYaml": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) var r any if err := yaml.Unmarshal(u.Bytes(args.Arguments[0].Export()), &r); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "save": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) filename := args.Str(0) var data []byte var err error if strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml") { data, err = yaml.Marshal(args.Any(1)) } else { data, err = json.Marshal(args.Any(1)) } if err == nil { if err = u.WriteFileBytes(gojs.FindPath(vm, filename), data); err != nil { panic(vm.NewGoError(err)) } return nil } else { panic(vm.NewGoError(err)) } }, "load": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) filename := args.Str(0) if data, err := u.ReadFileBytes(gojs.FindPath(vm, filename)); err == nil { var r any if strings.HasSuffix(filename, ".yml") || strings.HasSuffix(filename, ".yaml") { err = yaml.Unmarshal(data, &r) } else { err = json.Unmarshal(data, &r) } if err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } } else { panic(vm.NewGoError(err)) } }, "base64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(u.Base64(u.Bytes(args.Arguments[0].Export()))) }, "unBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(u.UnBase64(args.Str(0))) }, "urlBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(u.UrlBase64String(args.Str(0))) }, "unUrlBase64": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(u.UnUrlBase64(args.Str(0))) }, "hex": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(hex.EncodeToString(u.Bytes(args.Arguments[0].Export()))) }, "unHex": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) if r, err := hex.DecodeString(args.Str(0)); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "aes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(3) if r, err := u.EncryptAesBytes(u.Bytes(args.Arguments[0].Export()), u.Bytes(args.Arguments[1].Export()), u.Bytes(args.Arguments[2].Export())); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "unAes": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(3) if r, err := u.DecryptAesBytes(u.Bytes(args.Arguments[0].Export()), u.Bytes(args.Arguments[1].Export()), u.Bytes(args.Arguments[2].Export())); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "sm4": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(3) if r, err := sm4.CBCEncrypt(u.Bytes(args.Arguments[1].Export())[0:16], u.Bytes(args.Arguments[2].Export())[0:16], util.PKCS5Padding(u.Bytes(args.Arguments[0].Export()), 16)); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "unSM4": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(3) if r, err := sm4.CBCDecrypt(u.Bytes(args.Arguments[1].Export())[0:16], u.Bytes(args.Arguments[2].Export())[0:16], u.Bytes(args.Arguments[0].Export())); err == nil { if length := len(r); length > 0 { if unpadding := int(r[length-1]); length-unpadding >= 0 { r = util.PKCS5UnPadding(r) } } return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "genECDSA": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm) var pri, pub string var err error switch args.Int(0) { case 521: pri, pub, err = u.GenECDSA521Key() case 384: pri, pub, err = u.GenECDSA384Key() default: pri, pub, err = u.GenECDSA256Key() } if err == nil { return vm.ToValue([]string{pri, pub}) } else { panic(vm.NewGoError(err)) } }, "exportECDSAPrivateKey": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) keyStr := args.Str(0) var priKey *ecdsa.PrivateKey var err error if len(keyStr) >= 160 { priKey, err = u.MakeECDSA521PrivateKey(keyStr) } else if len(keyStr) >= 110 { priKey, err = u.MakeECDSA384PrivateKey(keyStr) } else { priKey, err = u.MakeECDSA256PrivateKey(keyStr) } if err != nil { panic(vm.NewGoError(err)) } priBytes, err := x509.MarshalPKCS8PrivateKey(priKey) if err != nil { panic(vm.NewGoError(err)) } pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "PRIVATE KEY", Bytes: priBytes, }) return vm.ToValue(string(pemBytes)) }, "exportECDSAPublicKey": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) keyStr := args.Str(0) var pubKey *ecdsa.PublicKey var err error if len(keyStr) >= 160 { pubKey, err = u.MakeECDSA521PublicKey(keyStr) } else if len(keyStr) >= 110 { pubKey, err = u.MakeECDSA384PublicKey(keyStr) } else { pubKey, err = u.MakeECDSA256PublicKey(keyStr) } if err != nil { panic(vm.NewGoError(err)) } pubBytes, err := x509.MarshalPKIXPublicKey(pubKey) if err != nil { panic(vm.NewGoError(err)) } pemBytes := pem.EncodeToMemory(&pem.Block{ Type: "PUBLIC KEY", Bytes: pubBytes, }) return vm.ToValue(string(pemBytes)) }, "importECDSAKey": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) keyText := args.Str(0) keyType := args.Str(1) var keyBytes []byte if strings.Contains(keyText, "-----BEGIN PRIVATE KEY-----") { keyType = "private" if block, _ := pem.Decode([]byte(keyText)); block == nil { panic(vm.NewGoError(errors.New("bad private pem key"))) } else { keyBytes = block.Bytes } } else if strings.Contains(keyText, "-----BEGIN PUBLIC KEY-----") { keyType = "public" if block, _ := pem.Decode([]byte(keyText)); block == nil { panic(vm.NewGoError(errors.New("bad public pem key"))) } else { keyBytes = block.Bytes } } else { var err error if keyBytes, err = base64.StdEncoding.DecodeString(keyText); err != nil { panic(vm.NewGoError(err)) } } if keyType == "private" { if priKey, err := x509.ParsePKCS8PrivateKey(keyBytes); err != nil { panic(vm.NewGoError(err)) } else { if ecdsaPriKey, ok := priKey.(*ecdsa.PrivateKey); !ok { panic(vm.NewGoError(errors.New("bad private key"))) } else { privateKey := base64.URLEncoding.EncodeToString(ecdsaPriKey.D.Bytes()) return vm.ToValue(privateKey) } } } else { if pubKey, err := x509.ParsePKIXPublicKey(keyBytes); err != nil { panic(vm.NewGoError(err)) } else { if ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey); !ok { panic(vm.NewGoError(errors.New("bad public key"))) } else { var buf bytes.Buffer buf.Write(ecdsaPubKey.X.Bytes()) buf.Write(ecdsaPubKey.Y.Bytes()) publicKey := base64.URLEncoding.EncodeToString(buf.Bytes()) return vm.ToValue(publicKey) } } } }, "signECDSA": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) data := args.Bytes(0) pri := args.Str(1) var priKey *ecdsa.PrivateKey var err error if len(pri) >= 80 { priKey, err = u.MakeECDSA521PrivateKey(pri) } else if len(pri) >= 55 { priKey, err = u.MakeECDSA384PrivateKey(pri) } else { priKey, err = u.MakeECDSA256PrivateKey(pri) } if err != nil { panic(vm.NewGoError(err)) } if signature, err := u.SignECDSA(data, priKey); err == nil { return vm.ToValue(signature) } else { panic(vm.NewGoError(err)) } }, "verifyECDSA": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(3) data := args.Bytes(0) signature := args.Str(1) pub := args.Str(2) var pubKey *ecdsa.PublicKey var err error if len(pub) >= 160 { pubKey, err = u.MakeECDSA521PublicKey(pub) } else if len(pub) >= 110 { pubKey, err = u.MakeECDSA384PublicKey(pub) } else { pubKey, err = u.MakeECDSA256PublicKey(pub) } if err != nil { panic(vm.NewGoError(err)) } return vm.ToValue(u.VerifyECDSA(data, signature, pubKey)) }, "encryptECDSA": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(3) data := args.Bytes(0) pub := args.Str(1) var pubKey *ecdsa.PublicKey var err error if len(pub) >= 80 { pubKey, err = u.MakeECDSA521PublicKey(pub) } else if len(pub) >= 60 { pubKey, err = u.MakeECDSA384PublicKey(pub) } else { pubKey, err = u.MakeECDSA256PublicKey(pub) } if err != nil { panic(vm.NewGoError(err)) } eciesPubKey := ecies.ImportECDSAPublic(pubKey) if r, err := ecies.Encrypt(u.GlobalRand1, eciesPubKey, data, nil, nil); err != nil { panic(vm.NewGoError(err)) } else { return vm.ToValue(r) } }, "decryptECDSA": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) data := args.Bytes(0) pri := args.Str(1) var priKey *ecdsa.PrivateKey var err error if len(pri) >= 160 { priKey, err = u.MakeECDSA521PrivateKey(pri) } else if len(pri) >= 110 { priKey, err = u.MakeECDSA384PrivateKey(pri) } else { priKey, err = u.MakeECDSA256PrivateKey(pri) } if err != nil { panic(vm.NewGoError(err)) } eciesPriKey := ecies.ImportECDSA(priKey) if r, err := eciesPriKey.Decrypt(u.GlobalRand1, data, nil, nil); err != nil { panic(vm.NewGoError(err)) } else { return vm.ToValue(r) } }, // TODO sm2 "gzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) if r, err := u.Gzip(u.Bytes(args.Arguments[0].Export())); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "gunzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) if r, err := u.Gunzip(u.Bytes(args.Arguments[0].Export())); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { return vm.ToValue(u.Id12()) }, "uniqueId": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { return vm.ToValue(u.UniqueId()) }, "token": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { size := 20 if len(argsIn.Arguments) > 0 { size = u.Int(argsIn.Arguments[0].Export()) } return vm.ToValue(u.MakeToken(size)) }, "md5": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) hash := md5.New() for _, v := range args.Array(0) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "sha1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) hash := sha1.New() for _, v := range args.Array(0) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "sha256": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) hash := sha256.New() for _, v := range args.Array(0) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "sha512": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) hash := sha512.New() for _, v := range args.Array(0) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "sm3": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) hash := sm3.New() for _, v := range args.Array(0) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "hmacMD5": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) hash := hmac.New(md5.New, args.Bytes(0)) for _, v := range args.Array(1) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "hmacSHA1": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) hash := hmac.New(sha1.New, args.Bytes(0)) for _, v := range args.Array(1) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "hmacSHA256": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) hash := hmac.New(sha256.New, args.Bytes(0)) for _, v := range args.Array(1) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "hmacSHA512": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) hash := hmac.New(sha512.New, args.Bytes(0)) for _, v := range args.Array(1) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "hmacSM3": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) hash := hmac.New(sm3.New, args.Bytes(0)) for _, v := range args.Array(1) { hash.Write(u.Bytes(v)) } return vm.ToValue(hash.Sum(nil)) }, "tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) var functions = map[string]any{} if len(argsIn.Arguments) > 2 { obj := argsIn.Arguments[2].ToObject(vm) for _, k := range obj.Keys() { v := obj.Get(k) if cb, ok := goja.AssertFunction(v); ok { functions[k] = func(in string) string { if r, err := cb(argsIn.This, vm.ToValue(in)); err == nil { return r.String() } return "" } } } } buf := bytes.NewBuffer(make([]byte, 0)) var err error tpl := template.New("tpl") if len(functions) > 0 { tpl.Funcs(functions) } if tpl, err = tpl.Parse(args.Str(0)); err == nil { err = tpl.Execute(buf, args.Arguments[1].Export()) } if err != nil { panic(vm.NewGoError(err)) } return vm.ToValue(buf.String()) }, "sleep": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) time.Sleep(time.Duration(args.Int64(0)) * time.Millisecond) return nil }, "setTimeout": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) callback := args.Func(0) timeout := time.Duration(args.Int64(1)) * time.Millisecond if callback != nil { go func() { if timeout > 0 { time.Sleep(timeout) } if _, err := callback(args.This, args.Arguments[2:]...); err != nil { args.Logger.Error(err.Error()) } }() } return nil }, "shell": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) a := make([]string, len(args.Arguments)-1) for i := 1; i < len(args.Arguments); i++ { a[i-1] = args.Str(i) } if r, err := u.RunCommand(args.Str(0), a...); err == nil { return vm.ToValue(r) } else { panic(vm.NewGoError(err)) } }, "toDatetime": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(time.UnixMilli(args.Int64(0)).Format("2006-01-02 15:04:05")) }, "fromDatetime": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) timeStr := args.Str(0) if len(timeStr) > 19 { timeStr = timeStr[0:19] } if strings.ContainsRune(timeStr, 'T') { timeStr = strings.ReplaceAll(timeStr, "T", " ") } if tm, err := time.ParseInLocation("2006-01-02 15:04:05", timeStr, time.Local); err == nil { return vm.ToValue(tm.UnixMilli()) } else { panic(vm.NewGoError(err)) } }, "toDate": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(time.UnixMilli(args.Int64(0)).Format("2006-01-02")) }, "fromDate": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) timeStr := args.Str(0) if len(timeStr) > 10 { timeStr = timeStr[0:10] } if tm, err := time.ParseInLocation("2006-01-02", timeStr, time.Local); err == nil { return vm.ToValue(tm.UnixMilli()) } else { panic(vm.NewGoError(err)) } }, "os": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { return vm.ToValue(runtime.GOOS) }, "arch": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { return vm.ToValue(runtime.GOARCH) }, "joinPath": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(filepath.Join(args.StrArray(0)...)) }, "getPathDir": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(filepath.Dir(args.Str(0))) }, "getPathBase": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(filepath.Base(args.Str(0))) }, "getPathVolume": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(filepath.VolumeName(args.Str(0))) }, "absPath": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) r, err := filepath.Abs(args.Str(0)) if err != nil { panic(vm.NewGoError(err)) } return vm.ToValue(r) }, "cleanPath": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(filepath.Clean(args.Str(0))) }, "isLocalPath": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) return vm.ToValue(filepath.IsLocal(args.Str(0))) }, } gojs.Register("apigo.cc/gojs/util", gojs.Module{ Object: obj, TsCode: utilTS, Example: utilMD, }) }