support hmac、ECDSA、SM3、SM4

This commit is contained in:
Star 2024-11-08 16:15:32 +08:00
parent 991b21616d
commit a405b83e69
4 changed files with 366 additions and 6 deletions

16
go.mod
View File

@ -3,20 +3,24 @@ module apigo.cc/gojs/util
go 1.18 go 1.18
require ( require (
apigo.cc/gojs v0.0.3 apigo.cc/gojs v0.0.4
github.com/ssgo/u v1.7.9 github.com/ssgo/u v1.7.11
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/ZZMarquis/gm v1.3.2 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/emmansun/gmsm v0.29.1 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/ssgo/config v1.7.7 // indirect github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9 // indirect
github.com/ssgo/config v1.7.9 // indirect
github.com/ssgo/log v1.7.7 // indirect github.com/ssgo/log v1.7.7 // indirect
github.com/ssgo/standard v1.7.7 // indirect github.com/ssgo/standard v1.7.7 // indirect
github.com/ssgo/tool v0.4.27 // indirect github.com/ssgo/tool v0.4.27 // indirect
golang.org/x/sys v0.26.0 // indirect golang.org/x/crypto v0.28.0 // indirect
golang.org/x/text v0.19.0 // indirect golang.org/x/sys v0.27.0 // indirect
golang.org/x/text v0.20.0 // indirect
) )

292
util.go
View File

@ -2,13 +2,19 @@ package util
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
"crypto/hmac"
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"crypto/x509"
_ "embed" _ "embed"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"encoding/pem"
"errors"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -17,6 +23,10 @@ import (
"apigo.cc/gojs" "apigo.cc/gojs"
"apigo.cc/gojs/goja" "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" "github.com/ssgo/u"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@ -158,6 +168,240 @@ func init() {
panic(vm.NewGoError(err)) 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 { "gzip": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(1) args := gojs.MakeArgs(&argsIn, vm).Check(1)
if r, err := u.Gzip(u.Bytes(args.Arguments[0].Export())); err == nil { if r, err := u.Gzip(u.Bytes(args.Arguments[0].Export())); err == nil {
@ -219,6 +463,54 @@ func init() {
} }
return vm.ToValue(hash.Sum(nil)) 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 { "tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(2) args := gojs.MakeArgs(&argsIn, vm).Check(2)
var functions = map[string]any{} var functions = map[string]any{}

32
util.ts
View File

@ -16,6 +16,16 @@ export default {
unHex, unHex,
aes, aes,
unAes, unAes,
sm4,
unSM4,
genECDSA,
exportECDSAPrivateKey,
exportECDSAPublicKey,
importECDSAKey,
signECDSA,
verifyECDSA,
encryptECDSA,
decryptECDSA,
gzip, gzip,
gunzip, gunzip,
id, id,
@ -25,6 +35,12 @@ export default {
sha1, sha1,
sha256, sha256,
sha512, sha512,
sm3,
hmacMD5,
hmacSHA1,
hmacSHA256,
hmacSHA512,
hmacSM3,
tpl, tpl,
sleep, sleep,
shell, shell,
@ -58,6 +74,16 @@ function hex(data: any): string { return '' }
function unHex(data: string): any { return null } function unHex(data: string): any { return null }
function aes(data: any, key: string, iv: string): string { return '' } function aes(data: any, key: string, iv: string): string { return '' }
function unAes(data: string, key: string, iv: string): any { return null } function unAes(data: string, key: string, iv: string): any { return null }
function sm4(data: any, key: string, iv: string): string { return '' }
function unSM4(data: string, key: string, iv: string): any { return null }
function genECDSA(): string[] { return ['priKey', 'pubKey'] }
function exportECDSAPrivateKey(key: string): string { return '' }
function exportECDSAPublicKey(key: string): string { return '' }
function importECDSAKey(pemKey: string): string { return '' }
function signECDSA(data: any, priKey: string): string { return '' }
function verifyECDSA(data: any, signature: string, pubKey: string): string { return '' }
function encryptECDSA(data: any, pubKey: string): string { return '' }
function decryptECDSA(data: any, priKey: string): string { return '' }
function gzip(data: any): string { return '' } function gzip(data: any): string { return '' }
function gunzip(data: string): any { return null } function gunzip(data: string): any { return null }
function id(): string { return '' } function id(): string { return '' }
@ -67,6 +93,12 @@ function md5(...data: any[]): string { return '' }
function sha1(...data: any[]): string { return '' } function sha1(...data: any[]): string { return '' }
function sha256(...data: any[]): string { return '' } function sha256(...data: any[]): string { return '' }
function sha512(...data: any[]): string { return '' } function sha512(...data: any[]): string { return '' }
function sm3(...data: any[]): string { return '' }
function hmacMD5(key: any, ...data: any[]): string { return '' }
function hmacSHA1(key: any, ...data: any[]): string { return '' }
function hmacSHA256(key: any, ...data: any[]): string { return '' }
function hmacSHA512(key: any, ...data: any[]): string { return '' }
function hmacSM3(key: any, ...data: any[]): string { return '' }
function tpl(text: string, data: any, functions?: Object): string { return '' } function tpl(text: string, data: any, functions?: Object): string { return '' }
function sleep(ms: number): void { } function sleep(ms: number): void { }
function setTimeout(callback: () => void, ms?: number, ...args: any): void { } function setTimeout(callback: () => void, ms?: number, ...args: any): void { }

View File

@ -2,12 +2,15 @@ package util_test
import ( import (
"bytes" "bytes"
"crypto/hmac"
"crypto/sha256"
"fmt" "fmt"
"testing" "testing"
"time" "time"
"apigo.cc/gojs" "apigo.cc/gojs"
_ "apigo.cc/gojs/util" _ "apigo.cc/gojs/util"
"github.com/ZZMarquis/gm/sm3"
"github.com/ssgo/u" "github.com/ssgo/u"
) )
@ -20,14 +23,43 @@ func TestHash(t *testing.T) {
testIsSame(vm, t, "util.sha1('hello',' 123')", u.Sha1([]byte("hello 123"))) testIsSame(vm, t, "util.sha1('hello',' 123')", u.Sha1([]byte("hello 123")))
testIsSame(vm, t, "util.sha256('hello 123')", u.Sha256([]byte("hello 123"))) testIsSame(vm, t, "util.sha256('hello 123')", u.Sha256([]byte("hello 123")))
testIsSame(vm, t, "util.sha512('hello 123')", u.Sha512([]byte("hello 123"))) testIsSame(vm, t, "util.sha512('hello 123')", u.Sha512([]byte("hello 123")))
sm3Hash := sm3.New()
sm3Hash.Write([]byte("hello 123"))
testIsSame(vm, t, "util.sm3('hello 123')", sm3Hash.Sum(nil))
hash := hmac.New(sha256.New, []byte("abc"))
hash.Write([]byte("hello 123"))
// hmacTestValue := u.HmacSha1([]byte("hello 123"),
testIsSame(vm, t, "util.hmacSHA256('abc', 'hello 123')", hash.Sum(nil))
testIsSame(vm, t, "util.json('hello 123')", u.Json("hello 123")) testIsSame(vm, t, "util.json('hello 123')", u.Json("hello 123"))
testIsSame(vm, t, "util.yaml('hello 123')", u.Yaml("hello 123")) testIsSame(vm, t, "util.yaml('hello 123')", u.Yaml("hello 123"))
testIsSame(vm, t, "util.aes('hello 123','12345678901234567','12345678901234567')", u.EncryptAes("hello 123", []byte("12345678901234567"), []byte("12345678901234567"))) testIsSame(vm, t, "util.aes('hello 123','12345678901234567','12345678901234567')", u.EncryptAes("hello 123", []byte("12345678901234567"), []byte("12345678901234567")))
sm4R, _ := vm.RunCode("util.hex(util.sm4('hello 123','12345678901234567','12345678901234567'))")
testIsSame(vm, t, "util.unSM4('"+u.String(sm4R)+"','12345678901234567','12345678901234567')", "hello 123")
testIsSame(vm, t, "util.gzip('hello 123')", u.GzipN([]byte("hello 123"))) testIsSame(vm, t, "util.gzip('hello 123')", u.GzipN([]byte("hello 123")))
tm, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-01-01 00:00:00", time.Local) tm, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-01-01 00:00:00", time.Local)
testIsSame(vm, t, "util.fromDatetime('2024-01-01 00:00:00')", tm.UnixMilli()) testIsSame(vm, t, "util.fromDatetime('2024-01-01 00:00:00')", tm.UnixMilli())
} }
func TestECDSA(t *testing.T) {
r, err := gojs.Run(`
import util from 'apigo.cc/gojs/util'
let [pri, pub] = util.genECDSA()
let text = 'hello 123'
let sign = util.signECDSA(text, pri)
let verify = util.verifyECDSA(text, sign, pub)
if(!verify) return 'failed to verify sign '+sign
return true
`, "")
if err != nil {
t.Fatal(err)
}
if r != true {
t.Fatal(r)
}
fmt.Println(u.Green("ecdsa test passed"))
}
func testIsSame(vm *gojs.Runtime, t *testing.T, code string, checkValue any) { func testIsSame(vm *gojs.Runtime, t *testing.T, code string, checkValue any) {
r, err := vm.RunCode(code) r, err := vm.RunCode(code)
if err != nil { if err != nil {