diff --git a/docs/IMPLEMENTATION.md b/docs/IMPLEMENTATION.md index 87bab66..4741443 100644 --- a/docs/IMPLEMENTATION.md +++ b/docs/IMPLEMENTATION.md @@ -9,7 +9,7 @@ - [X] Set up testing framework and utilities - [ ] **Challenge Generation & HMAC Security** - - [ ] Implement HMAC-signed challenge generation (stateless) + - [X] Implement HMAC-signed challenge generation (stateless) - [ ] Create challenge authenticity verification - [ ] Add timestamp validation for replay protection (5 minutes TTL) - [ ] Implement canonical challenge field ordering for HMAC diff --git a/internal/pow/generator.go b/internal/pow/generator.go new file mode 100644 index 0000000..52bea82 --- /dev/null +++ b/internal/pow/generator.go @@ -0,0 +1,62 @@ +package pow + +import ( + "crypto/rand" + "time" +) + +// Generator handles creation of PoW challenges with HMAC signing +type Generator struct { + config *Config +} + +// GenerateOption represents a functional option for challenge generation +type generateOption func(*Challenge) + +// WithDifficulty sets custom difficulty for the challenge +func WithDifficulty(difficulty int) generateOption { + return func(c *Challenge) { + c.Difficulty = difficulty + } +} + +// NewGenerator creates a new challenge generator +func NewGenerator(config *Config) *Generator { + return &Generator{ + config: config, + } +} + +// GenerateChallenge creates a new HMAC-signed PoW challenge +func (g *Generator) GenerateChallenge(opts ...generateOption) (*Challenge, error) { + // Create challenge with defaults (no random data yet) + challenge := &Challenge{ + Timestamp: time.Now().Unix(), + Difficulty: g.config.DefaultDifficulty, + Resource: g.config.Resource, + } + + // Apply options + for _, opt := range opts { + opt(challenge) + } + + // Validate difficulty bounds + if challenge.Difficulty < g.config.MinDifficulty || challenge.Difficulty > g.config.MaxDifficulty { + return nil, ErrInvalidDifficulty + } + + // Generate cryptographic random data + challenge.Random = g.generateRandom(g.config.RandomBytes) + + // Sign the challenge with HMAC + challenge.Sign(g.config.HMACSecret) + return challenge, nil +} + +// generateRandom creates cryptographic random bytes +func (g *Generator) generateRandom(length int) []byte { + bytes := make([]byte, length) + rand.Read(bytes) + return bytes +}