mirror of
https://github.com/luxfi/esign.git
synced 2026-10-30 16:58:18 +00:00
This commit lands the first end-to-end signable surface:
* pkg/sign — Signer/Verifier interfaces, Ed25519 implementation
(stdlib crypto/ed25519), method-dispatch helper. 8 unit tests:
round-trip, deterministic-from-seed, bad-seed-length, tampered
payload + sig, bad pubkey, bad sig length, zero-value verifier.
* pkg/envelope — added Signature.SignerHashHex() and SeqString
helper used by the verifier-side error reporting.
* pkg/api — Handler with pluggable Store, in-memory MemStore for
tests + small deployments. CreateRequest / SignRequest /
TransitionRequest / VerifyResponse types form the wire surface.
Handler.Sign runs cryptographic verify BEFORE calling
envelope.AddSignature so a malformed sig never lands in the
envelope. 3 e2e tests cover happy path, sig tampering detection,
and crypto-fails-before-store ordering.
* pkg/api — opcode table (Create/Get/Sign/Transition/Verify/Audit
+ Ping). ZAPServer serves [4B BE length][2B LE opcode][JSON body]
framing with per-conn pipelining + idle timeout. Tests: ping,
end-to-end create→sent→sign→verify, unknown opcode error,
bad-JSON error.
* cmd/esignd — standalone daemon. `--zap :9650` by default.
SIGINT/SIGTERM shutdown drains inflight conns before exit.
Test summary: 3 packages, 22 tests, all green.
69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
// Copyright 2026 Lux Industries Inc. and the luxfi/esign authors.
|
|
// Licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
|
|
package sign
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
)
|
|
|
|
// Ed25519Signer wraps a stdlib ed25519.PrivateKey. The zero value is not
|
|
// usable; call NewEd25519Signer to construct one, or build with a known
|
|
// seed using NewEd25519FromSeed.
|
|
type Ed25519Signer struct {
|
|
priv ed25519.PrivateKey
|
|
pub ed25519.PublicKey
|
|
}
|
|
|
|
// NewEd25519Signer returns a freshly-keyed signer using crypto/rand.
|
|
func NewEd25519Signer() (*Ed25519Signer, error) {
|
|
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Ed25519Signer{priv: priv, pub: pub}, nil
|
|
}
|
|
|
|
// NewEd25519FromSeed deterministically derives a signer from a 32-byte
|
|
// seed. Useful for tests and KMS-managed keys where the seed comes back
|
|
// over the wire.
|
|
func NewEd25519FromSeed(seed []byte) (*Ed25519Signer, error) {
|
|
if len(seed) != ed25519.SeedSize {
|
|
return nil, ErrBadPubKey
|
|
}
|
|
priv := ed25519.NewKeyFromSeed(seed)
|
|
pub := priv.Public().(ed25519.PublicKey)
|
|
return &Ed25519Signer{priv: priv, pub: pub}, nil
|
|
}
|
|
|
|
// Sign returns a 64-byte Ed25519 signature over payload.
|
|
func (s *Ed25519Signer) Sign(payload []byte) ([]byte, error) {
|
|
return ed25519.Sign(s.priv, payload), nil
|
|
}
|
|
|
|
// Public returns the 32-byte Ed25519 public key.
|
|
func (s *Ed25519Signer) Public() []byte {
|
|
out := make([]byte, len(s.pub))
|
|
copy(out, s.pub)
|
|
return out
|
|
}
|
|
|
|
// Ed25519Verifier verifies Ed25519 signatures. Stateless — no key
|
|
// material held; the public key arrives per Verify call.
|
|
type Ed25519Verifier struct{}
|
|
|
|
// Verify checks a 64-byte Ed25519 sig against pubKey (32 bytes) and payload.
|
|
func (Ed25519Verifier) Verify(pubKey, payload, sig []byte) error {
|
|
if len(pubKey) != ed25519.PublicKeySize {
|
|
return ErrBadPubKey
|
|
}
|
|
if len(sig) != ed25519.SignatureSize {
|
|
return ErrBadSignature
|
|
}
|
|
if !ed25519.Verify(ed25519.PublicKey(pubKey), payload, sig) {
|
|
return ErrBadSignature
|
|
}
|
|
return nil
|
|
}
|