ZRY il y a 1 mois
Parent
commit
9542ddbd13
15 fichiers modifiés avec 437 ajouts et 24 suppressions
  1. 1 24
      .gitignore
  2. 8 0
      .idea/.gitignore
  3. 11 0
      .idea/NagaeEDST.iml
  4. 7 0
      .idea/misc.xml
  5. 8 0
      .idea/modules.xml
  6. 6 0
      .idea/vcs.xml
  7. 25 0
      cmd/.gitignore
  8. 78 0
      cmd/cmd_keygen.go
  9. 81 0
      cmd/cmd_sv.go
  10. 11 0
      cmd/go.mod
  11. 8 0
      cmd/go.sum
  12. 42 0
      cmd/key_store.go
  13. 105 0
      cmd/main.go
  14. 36 0
      cmd/path.go
  15. 10 0
      justfile

+ 1 - 24
.gitignore

@@ -1,26 +1,3 @@
-# ---> Go
-# Compiled Object files, Static and Dynamic libs (Shared Objects)
-*.o
-*.a
-*.so
 
-# Folders
-_obj
-_test
-
-# Architecture specific extensions/prefixes
-*.[568vq]
-[568vq].out
-
-*.cgo1.go
-*.cgo2.c
-_cgo_defun.c
-_cgo_gotypes.go
-_cgo_export.*
-
-_testmain.go
-
-*.exe
-*.test
-*.prof
+dist
 

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 11 - 0
.idea/NagaeEDST.iml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/dist" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="XMakeProjectSettings">
+    <option name="currentArchitecture" value="x86" />
+    <option name="workingDirectory" value="$PROJECT_DIR$" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/NagaeEDST.iml" filepath="$PROJECT_DIR$/.idea/NagaeEDST.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 25 - 0
cmd/.gitignore

@@ -0,0 +1,25 @@
+# ---> Go
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof

+ 78 - 0
cmd/cmd_keygen.go

@@ -0,0 +1,78 @@
+package main
+
+import (
+	"bytes"
+	"crypto/ed25519"
+	"crypto/rand"
+	"encoding/ascii85"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"github.com/urfave/cli/v2"
+	"os"
+	"path"
+)
+
+func KeyGenerate(c *cli.Context) error {
+	cp := GetKeyStorePath(c)
+	name := c.String("name")
+	if name == "" {
+		return fmt.Errorf("flag 'name' should not be empty")
+	}
+	fpath := path.Join(cp, fmt.Sprintf("%s.key.json", name))
+	_, err := os.Stat(fpath)
+	if err == nil {
+		return fmt.Errorf("key %s already exists", name)
+	} else {
+		if !os.IsNotExist(err) {
+			return fmt.Errorf("failed use stat to check key file '%s.key.json' existance: %w", name, err)
+		}
+	}
+	pubkey, prikey, err := ed25519.GenerateKey(rand.Reader)
+	if err != nil {
+		return fmt.Errorf("failed to generate ed25519 key: %w", err)
+	}
+	EncodedPubKey := base64.URLEncoding.EncodeToString(pubkey)
+	EncodedPriKey := base64.URLEncoding.EncodeToString(prikey)
+	jdat := KeyStore{
+		PublicKey:  EncodedPubKey,
+		PrivateKey: EncodedPriKey,
+	}
+	jstr, err := json.Marshal(jdat)
+	if err != nil {
+		return fmt.Errorf("failed to marshal key store: %w", err)
+	}
+	err = os.WriteFile(fpath, jstr, 0660)
+	if err != nil {
+		return fmt.Errorf("failed to write key store: %w", err)
+	}
+	fmt.Println("Done.")
+	return nil
+}
+
+func PrintPublicKey(c *cli.Context) error {
+	pub, _, err := LoadKey(c)
+	if err != nil {
+		return err
+	}
+	fmt.Println("==== Base64 URL Encoding ====")
+	fmt.Println(base64.URLEncoding.EncodeToString(pub))
+	fmt.Println("==== Base64 Std Encoding ====")
+	fmt.Println(base64.StdEncoding.EncodeToString(pub))
+	fmt.Println("==== Hex Encoding ====")
+	fmt.Println(hex.EncodeToString(pub))
+	fmt.Println("==== Ascii85 Encoding ====")
+	buf := bytes.NewBuffer([]byte{})
+	a85enc := ascii85.NewEncoder(buf)
+	_, err = a85enc.Write(pub)
+	if err != nil {
+		return fmt.Errorf("failed to write to ascii85 encoder: %w", err)
+	}
+	err = a85enc.Close()
+	if err != nil {
+		return fmt.Errorf("failed to close ascii85 encoder: %w", err)
+	}
+	fmt.Println(buf.String())
+	return nil
+}

+ 81 - 0
cmd/cmd_sv.go

@@ -0,0 +1,81 @@
+package main
+
+import (
+	"crypto"
+	"crypto/ed25519"
+	"crypto/rand"
+	"fmt"
+	"github.com/urfave/cli/v2"
+	"os"
+)
+
+func SignFile(c *cli.Context) error {
+	_, prvBin, err := LoadKey(c)
+	if err != nil {
+		return err
+	}
+	if len(prvBin) != ed25519.PrivateKeySize {
+		return fmt.Errorf("invalid private key size: should be %d, got %d", ed25519.PrivateKeySize, len(prvBin))
+	}
+	prvKey := ed25519.PrivateKey(prvBin)
+	filename := c.String("file")
+	output := c.String("out")
+	if filename == "" {
+		return fmt.Errorf("flag 'file' should not be empty")
+	}
+	if output == "" {
+		output = filename + ".ng-edst-sig"
+	}
+	fbin, err := os.ReadFile(filename)
+	if err != nil {
+		return fmt.Errorf("failed to read file '%s': %w", filename, err)
+	}
+	sig, err := prvKey.Sign(rand.Reader, fbin, &ed25519.Options{
+		Hash:    crypto.Hash(0),
+		Context: "",
+	})
+	if err != nil {
+		return fmt.Errorf("failed to sign with ed25519: %w", err)
+	}
+	err = os.WriteFile(output, sig, 0660)
+	if err != nil {
+		return fmt.Errorf("failed to write file: %w", err)
+	}
+	return nil
+}
+func VerifyFile(c *cli.Context) error {
+	pubBin, _, err := LoadKey(c)
+	if err != nil {
+		return err
+	}
+	if len(pubBin) != ed25519.PublicKeySize {
+		return fmt.Errorf("invalid public key size: should be %d but got %d", ed25519.PublicKeySize, len(pubBin))
+	}
+	pubKey := ed25519.PublicKey(pubBin)
+	filename := c.String("file")
+	sigfile := c.String("sig")
+	if filename == "" {
+		return fmt.Errorf("flag 'file' should not be empty")
+	}
+	if sigfile == "" {
+		sigfile = filename + ".ng-edst-sig"
+	}
+	fbin, err := os.ReadFile(filename)
+	if err != nil {
+		return fmt.Errorf("failed to read file '%s': %w", filename, err)
+	}
+	sbin, err := os.ReadFile(sigfile)
+	if err != nil {
+		return fmt.Errorf("failed to read sig file '%s': %w", sigfile, err)
+	}
+	err = ed25519.VerifyWithOptions(pubKey, fbin, sbin, &ed25519.Options{
+		Hash:    crypto.Hash(0),
+		Context: "",
+	})
+	if err == nil {
+		fmt.Println("Verify OK.")
+	} else {
+		fmt.Println("verify FAIL: ", err)
+	}
+	return nil
+}

+ 11 - 0
cmd/go.mod

@@ -0,0 +1,11 @@
+module git.swzry.com/ProjectNagae/NagaeEDST/cmd
+
+go 1.21.6
+
+require github.com/urfave/cli/v2 v2.27.1
+
+require (
+	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
+	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
+)

+ 8 - 0
cmd/go.sum

@@ -0,0 +1,8 @@
+github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
+github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
+github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
+github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=

+ 42 - 0
cmd/key_store.go

@@ -0,0 +1,42 @@
+package main
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/urfave/cli/v2"
+	"os"
+	"path"
+)
+
+type KeyStore struct {
+	PublicKey  string `json:"public_key"`
+	PrivateKey string `json:"private_key"`
+}
+
+func LoadKey(c *cli.Context) (pub []byte, prv []byte, rerr error) {
+	cp := GetKeyStorePath(c)
+	keyName := c.String("key")
+	if keyName == "" {
+		return nil, nil, fmt.Errorf("flag 'key' should not be empty")
+	}
+	keyFile := path.Join(cp, fmt.Sprintf("%s.key.json", keyName))
+	jbin, err := os.ReadFile(keyFile)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to read key file: %w", err)
+	}
+	var jdat KeyStore
+	err = json.Unmarshal(jbin, &jdat)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to unmarshal key file: %w", err)
+	}
+	pub, err = base64.URLEncoding.DecodeString(jdat.PublicKey)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to decode public key: %w", err)
+	}
+	prv, err = base64.URLEncoding.DecodeString(jdat.PrivateKey)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to decode private key: %w", err)
+	}
+	return pub, prv, nil
+}

+ 105 - 0
cmd/main.go

@@ -0,0 +1,105 @@
+package main
+
+import (
+	"fmt"
+	cli "github.com/urfave/cli/v2"
+	"os"
+)
+
+func main() {
+	app := &cli.App{
+		Name:  "NagaeEDST",
+		Usage: "Nagae ED25519 Signature Tool",
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:     "key-store",
+				Aliases:  []string{"ks"},
+				Usage:    "Key store path, default is '%%LOCAL_CREDENTIALS%%/NagaeROV/NagaeEDST'",
+				Required: false,
+			},
+		},
+		Commands: []*cli.Command{
+			{
+				Name:    "keygen",
+				Aliases: []string{"kg"},
+				Usage:   "Generate a new key",
+				Flags: []cli.Flag{
+					&cli.StringFlag{
+						Name:     "name",
+						Aliases:  []string{"n"},
+						Usage:    "Name of the key",
+						Required: true,
+					},
+				},
+				Action: KeyGenerate,
+			},
+			{
+				Name:    "print-pubkey",
+				Aliases: []string{"pub"},
+				Flags: []cli.Flag{
+					&cli.StringFlag{
+						Name:     "key",
+						Usage:    "key name",
+						Aliases:  []string{"k"},
+						Required: true,
+					},
+				},
+				Action: PrintPublicKey,
+			},
+			{
+				Name:    "sign-file",
+				Aliases: []string{"sf"},
+				Usage:   "Sign a file",
+				Flags: []cli.Flag{
+					&cli.StringFlag{
+						Name:     "file",
+						Aliases:  []string{"f"},
+						Required: true,
+					},
+					&cli.StringFlag{
+						Name:     "out",
+						Usage:    "specify output filename, default is <file>.ng-edst-sig",
+						Aliases:  []string{"o"},
+						Required: false,
+					},
+					&cli.StringFlag{
+						Name:     "key",
+						Usage:    "key name",
+						Aliases:  []string{"k"},
+						Required: true,
+					},
+				},
+				Action: SignFile,
+			},
+			{
+				Name:    "verify-file",
+				Aliases: []string{"vf"},
+				Usage:   "verify a file",
+				Flags: []cli.Flag{
+					&cli.StringFlag{
+						Name:     "file",
+						Aliases:  []string{"f"},
+						Required: true,
+					},
+					&cli.StringFlag{
+						Name:     "sig",
+						Usage:    "specify signature filename, default is <file>.ng-edst-sig",
+						Aliases:  []string{"s"},
+						Required: false,
+					},
+					&cli.StringFlag{
+						Name:     "key",
+						Usage:    "key name",
+						Aliases:  []string{"k"},
+						Required: true,
+					},
+				},
+				Action: VerifyFile,
+			},
+		},
+	}
+	if err := app.Run(os.Args); err != nil {
+		fmt.Println("command failed with error: ", err)
+		os.Exit(-5)
+	}
+}

+ 36 - 0
cmd/path.go

@@ -0,0 +1,36 @@
+package main
+
+import (
+	"fmt"
+	"github.com/urfave/cli/v2"
+	"os"
+	"path"
+)
+
+func GetKeyStorePath(c *cli.Context) string {
+	ks := c.String("key-store")
+	if ks != "" {
+		return ks
+	}
+	credentialRoot := os.Getenv("LOCAL_CREDENTIALS")
+	if credentialRoot == "" {
+		fmt.Println("env var 'LOCAL_CREDENTIALS' is not set")
+		os.Exit(-1)
+	}
+	dfi, err := os.Stat(credentialRoot)
+	if err != nil {
+		fmt.Println("failed to stat credential root directory: ", err)
+		os.Exit(-2)
+	}
+	if !dfi.IsDir() {
+		fmt.Println("credential root is not a directory")
+		os.Exit(-3)
+	}
+	thisCredential := path.Join(credentialRoot, "NagaeROV", "NagaeEDST")
+	err = os.MkdirAll(thisCredential, 0770)
+	if err != nil {
+		fmt.Println("failed to create credential storage directory: ", err)
+		os.Exit(-4)
+	}
+	return thisCredential
+}

+ 10 - 0
justfile

@@ -0,0 +1,10 @@
+jfdir := replace(justfile_directory(), "\\", "/")
+
+dist := jfdir / "dist"
+cmd_src := jfdir / "cmd"
+
+build:
+	cd {{cmd_src}}; go build -o {{dist / "NagaeEDST.exe"}}
+
+run *args:
+	cd {{dist}}; ./NagaeEDST.exe {{args}}