Рыба проекта. Минимальная функциональность

This commit is contained in:
2026-08-03 22:22:24 +03:00
commit 8c8631ac9c
80 changed files with 10618 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
package crypto
import "io"
// Decryptor defines the contract for decrypting ciphertext using private keys.
type Decryptor interface {
Decrypt(
src io.Reader,
privs []RecipientPriv,
plaintext io.Writer,
) error
}
+13
View File
@@ -0,0 +1,13 @@
package crypto
import "io"
// Encryptor defines the contract for encrypting plaintext to multiple recipients.
type Encryptor interface {
Encrypt(
plaintext io.Reader,
recipients []RecipientPub,
sink io.Writer,
rand io.Reader,
) error
}
+50
View File
@@ -0,0 +1,50 @@
package crypto
import "io"
// KEM defines the contract for a Key Encapsulation Mechanism.
type KEM interface {
SchemeID() uint16
GenerateKeyPair(
rand io.Reader,
) (
RecipientPub,
RecipientPriv,
error,
)
Encapsulate(
pub RecipientPub,
rand io.Reader,
) (
ciphertext []byte,
sharedSecret []byte,
err error,
)
Decapsulate(
priv RecipientPriv,
ciphertext []byte,
) (
sharedSecret []byte,
err error,
)
LoadPriv(
raw []byte,
) (
RecipientPriv,
error,
)
}
// RecipientPub represents a public recipient key.
type RecipientPub interface {
SchemeID() uint16
KeyID() []byte
Raw() []byte
}
// RecipientPriv represents a private recipient key.
type RecipientPriv interface {
SchemeID() uint16
KeyID() []byte
Raw() []byte
}
+27
View File
@@ -0,0 +1,27 @@
package crypto
import "io"
// KeyManager defines the contract for loading and generating recipient keys.
type KeyManager interface {
LoadPub(
path string,
schemeID uint16,
) (
RecipientPub,
error,
)
LoadPriv(
path string,
schemeID uint16,
) (
RecipientPriv,
error,
)
Generate(
schemeID uint16,
pubOut io.Writer,
privOut io.Writer,
rand io.Reader,
) error
}
+84
View File
@@ -0,0 +1,84 @@
package crypto
import (
"errors"
"fmt"
"sync"
)
// Sentinel errors for registry operations.
var (
ErrUnknownScheme = errors.New("unknown KEM scheme")
ErrDuplicateScheme = errors.New("duplicate KEM scheme registration")
)
// KEMFactory is a constructor function that returns a fresh KEM instance.
type KEMFactory func() KEM
// Registry is a concurrent-safe map of KEM scheme IDs to their factories.
type Registry interface {
Register(
schemeID uint16,
factory KEMFactory,
) error
Lookup(
schemeID uint16,
) (
KEMFactory,
error,
)
}
// registry is the private implementation of Registry.
type registry struct {
mutex sync.RWMutex
factories map[uint16]KEMFactory
}
// NewRegistry creates a new empty Registry.
func NewRegistry() Registry {
return &registry{
factories: make(map[uint16]KEMFactory),
}
}
// Register adds a KEMFactory for the given schemeID.
// Returns ErrDuplicateScheme if the schemeID is already registered.
func (r *registry) Register(schemeID uint16, factory KEMFactory) error {
r.mutex.Lock()
defer r.mutex.Unlock()
if _, exists := r.factories[schemeID]; exists {
return fmt.Errorf(
"scheme 0x%04x: %w",
schemeID,
ErrDuplicateScheme,
)
}
r.factories[schemeID] = factory
return nil
}
// Lookup retrieves the KEMFactory for the given schemeID.
// Returns ErrUnknownScheme if the schemeID is not registered.
func (r *registry) Lookup(
schemeID uint16,
) (
KEMFactory,
error,
) {
r.mutex.RLock()
defer r.mutex.RUnlock()
factory, exists := r.factories[schemeID]
if !exists {
return nil, fmt.Errorf(
"scheme 0x%04x: %w",
schemeID,
ErrUnknownScheme,
)
}
return factory, nil
}
+173
View File
@@ -0,0 +1,173 @@
package crypto
import (
"errors"
"io"
"sync"
"testing"
)
// mockKEM is a minimal KEM implementation for testing the registry.
type mockKEM struct {
schemeID uint16
}
func (m *mockKEM) SchemeID() uint16 {
return m.schemeID
}
func (m *mockKEM) GenerateKeyPair(
rand io.Reader,
) (
RecipientPub,
RecipientPriv,
error,
) {
return nil, nil, nil
}
func (m *mockKEM) Encapsulate(
pub RecipientPub,
rand io.Reader,
) (
ciphertext []byte,
sharedSecret []byte,
err error,
) {
return nil, nil, nil
}
func (m *mockKEM) Decapsulate(
priv RecipientPriv,
ciphertext []byte,
) (
sharedSecret []byte,
err error,
) {
return nil, nil
}
func (m *mockKEM) LoadPriv(
raw []byte,
) (
RecipientPriv,
error,
) {
return &mockRecipient{schemeIDValue: m.schemeID, rawBytes: raw}, nil
}
// mockRecipient implements RecipientPub and RecipientPriv for tests.
type mockRecipient struct {
schemeIDValue uint16
rawBytes []byte
}
func (r *mockRecipient) SchemeID() uint16 {
return r.schemeIDValue
}
func (r *mockRecipient) KeyID() []byte {
return r.rawBytes
}
func (r *mockRecipient) Raw() []byte {
return r.rawBytes
}
func TestRegistry_RegisterAndLookup(t *testing.T) {
registry := NewRegistry()
factory := func() KEM {
return &mockKEM{schemeID: 0x0001}
}
err := registry.Register(0x0001, factory)
if err != nil {
t.Fatalf("unexpected error registering scheme: %v", err)
}
foundFactory, err := registry.Lookup(0x0001)
if err != nil {
t.Fatalf("unexpected error looking up scheme: %v", err)
}
kem := foundFactory()
if kem.SchemeID() != 0x0001 {
t.Errorf("expected schemeID 0x0001, got 0x%04x", kem.SchemeID())
}
}
func TestRegistry_LookupUnknownScheme(t *testing.T) {
registry := NewRegistry()
_, err := registry.Lookup(0x9999)
if err == nil {
t.Fatal("expected error for unknown scheme, got nil")
}
if !errors.Is(err, ErrUnknownScheme) {
t.Errorf("expected ErrUnknownScheme, got %v", err)
}
}
func TestRegistry_RegisterDuplicateScheme(t *testing.T) {
registry := NewRegistry()
factory := func() KEM {
return &mockKEM{schemeID: 0x0001}
}
err := registry.Register(0x0001, factory)
if err != nil {
t.Fatalf("unexpected error on first register: %v", err)
}
err = registry.Register(0x0001, factory)
if err == nil {
t.Fatal("expected error for duplicate scheme, got nil")
}
if !errors.Is(err, ErrDuplicateScheme) {
t.Errorf("expected ErrDuplicateScheme, got %v", err)
}
}
func TestRegistry_ConcurrentAccess(t *testing.T) {
registry := NewRegistry()
const goroutines = 100
var waitGroup sync.WaitGroup
waitGroup.Add(goroutines)
for index := range goroutines {
go func(schemeID uint16) {
defer waitGroup.Done()
factory := func() KEM {
return &mockKEM{schemeID: schemeID}
}
_ = registry.Register(schemeID, factory)
_, _ = registry.Lookup(schemeID)
}(uint16(index + 1))
}
waitGroup.Wait()
}
func TestRegistry_LookupReturnsIndependentInstances(t *testing.T) {
registry := NewRegistry()
factory := func() KEM {
return &mockKEM{schemeID: 0x0001}
}
_ = registry.Register(0x0001, factory)
factoryOne, _ := registry.Lookup(0x0001)
factoryTwo, _ := registry.Lookup(0x0001)
kemOne := factoryOne()
kemTwo := factoryTwo()
if kemOne == kemTwo {
t.Error("expected independent KEM instances from factory")
}
}