Files
EveryPublish/client/core/internal/device/device.go
T

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))
}