package identity import ( "crypto/ed25519" "crypto/rand" "encoding/hex" "encoding/json" "fmt" "os" "strings" ) type Identity struct { PublicKey ed25519.PublicKey `json:"public_key"` PrivateKey ed25519.PrivateKey `json:"private_key"` Address Address `json:"address"` } type Address [5]byte func (a Address) String() string { return hex.EncodeToString(a[:]) } func (a Address) MarshalText() ([]byte, error) { return []byte(a.String()), nil } func (a *Address) UnmarshalText(text []byte) error { decoded, err := hex.DecodeString(string(text)) if err != nil { return err } if len(decoded) != 5 { return fmt.Errorf("address must be 5 bytes") } copy(a[:], decoded) return nil } func AddressFromPublicKey(pubKey []byte) Address { var addr Address h := hashBytes(pubKey) copy(addr[:], h[:5]) return addr } func Generate() *Identity { pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { panic(err) } return &Identity{ PublicKey: pub, PrivateKey: priv, Address: AddressFromPublicKey(pub), } } func (id *Identity) PublicKeyHex() string { return hex.EncodeToString(id.PublicKey) } func (id *Identity) PrivateKeyHex() string { return hex.EncodeToString(id.PrivateKey) } func (id *Identity) Sign(data []byte) []byte { return ed25519.Sign(id.PrivateKey, data) } func (id *Identity) Verify(data, sig []byte) bool { return ed25519.Verify(id.PublicKey, data, sig) } func LoadOrGenerate(path string) (*Identity, error) { data, err := os.ReadFile(path) if err == nil { return Parse(strings.TrimSpace(string(data))) } if !os.IsNotExist(err) { return nil, err } id := Generate() encoded, err := id.Serialize() if err != nil { return nil, err } if err := os.MkdirAll(dir(path), 0755); err != nil { return nil, err } if err := os.WriteFile(path, []byte(encoded), 0600); err != nil { return nil, err } return id, nil } func (id *Identity) Serialize() (string, error) { data, err := json.Marshal(id) if err != nil { return "", err } return string(data), nil } func Parse(data string) (*Identity, error) { var id Identity if err := json.Unmarshal([]byte(data), &id); err != nil { return nil, fmt.Errorf("parse identity: %w", err) } if len(id.PrivateKey) == 0 { return nil, fmt.Errorf("invalid identity: no private key") } id.Address = AddressFromPublicKey(id.PublicKey) return &id, nil } func dir(path string) string { idx := strings.LastIndex(path, "/") if idx == -1 { idx = strings.LastIndex(path, "\\") } if idx == -1 { return "." } return path[:idx] } func hashBytes(data []byte) []byte { h := make([]byte, 32) for i, b := range data { h[i%32] ^= b } // simple hash expansion for round := 0; round < 3; round++ { for i := 0; i < 32; i++ { h[i] = h[i] ^ h[(i+1)%32] ^ h[(i+7)%32] h[i] = (h[i] << 3) | (h[i] >> 5) } } return h }