73 lines
1.9 KiB
Go
73 lines
1.9 KiB
Go
package device
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// Identity 设备身份:Ed25519 密钥对 + 服务器分配的设备 ID
|
|
type Identity struct {
|
|
DeviceID uint64 `json:"deviceId"`
|
|
Name string `json:"name"`
|
|
PublicKey string `json:"publicKey"`
|
|
priv ed25519.PrivateKey `json:"-"`
|
|
}
|
|
|
|
// LoadOrCreate 加载或生成本机设备身份(私钥 hex 文件 0600)
|
|
func LoadOrCreate(dir string, name string) (*Identity, error) {
|
|
keyPath := filepath.Join(dir, "device.key")
|
|
idPath := filepath.Join(dir, "identity.json")
|
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
|
return nil, err
|
|
}
|
|
var priv ed25519.PrivateKey
|
|
if raw, err := os.ReadFile(keyPath); err == nil {
|
|
seed, err := hex.DecodeString(string(raw))
|
|
if err != nil || len(seed) != ed25519.SeedSize {
|
|
return nil, errors.New("device key corrupted")
|
|
}
|
|
priv = ed25519.NewKeyFromSeed(seed)
|
|
} else {
|
|
_, priv, err = ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = os.WriteFile(keyPath, []byte(hex.EncodeToString(priv.Seed())), 0o600); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
id := &Identity{
|
|
Name: name,
|
|
PublicKey: base64.StdEncoding.EncodeToString(priv.Public().(ed25519.PublicKey)),
|
|
priv: priv,
|
|
}
|
|
if raw, err := os.ReadFile(idPath); err == nil {
|
|
_ = json.Unmarshal(raw, id)
|
|
id.priv = priv
|
|
if id.Name == "" {
|
|
id.Name = name
|
|
}
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// Save 持久化设备 ID(配对成功后调用)
|
|
func (i *Identity) Save(dir string) error {
|
|
raw, err := json.MarshalIndent(i, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return os.WriteFile(filepath.Join(dir, "identity.json"), raw, 0o600)
|
|
}
|
|
|
|
// Sign 对载荷签名(hello 握手用)
|
|
func (i *Identity) Sign(payload []byte) string {
|
|
return base64.StdEncoding.EncodeToString(ed25519.Sign(i.priv, payload))
|
|
}
|