Add ability to pass different flags to the DEST GENERATE function

This commit is contained in:
eyedeekay
2024-12-08 15:48:46 -05:00
parent 8e42fd9a18
commit 9694fe011c
5 changed files with 128 additions and 125 deletions

View File

@ -13,7 +13,6 @@ const (
// Domain suffixes // Domain suffixes
I2PDomainSuffix = ".i2p" I2PDomainSuffix = ".i2p"
B32DomainSuffix = ".b32.i2p"
) )
// I2PAddr represents an I2P destination, equivalent to an IP address. // I2PAddr represents an I2P destination, equivalent to an IP address.
@ -66,8 +65,8 @@ func validateAddressFormat(addr string) error {
len(addr), MinAddressLength, MaxAddressLength) len(addr), MinAddressLength, MaxAddressLength)
} }
if strings.HasSuffix(addr, B32DomainSuffix) { if strings.HasSuffix(addr, B32Suffix) {
return fmt.Errorf("cannot convert %s to full destination", B32DomainSuffix) return fmt.Errorf("cannot convert %s to full destination", B32Suffix)
} }
return nil return nil

View File

@ -10,53 +10,53 @@ import (
) )
var ( var (
ErrInvalidKeyType = errors.New("invalid key type") ErrInvalidKeyType = errors.New("invalid key type")
ErrSigningFailed = errors.New("signing operation failed") ErrSigningFailed = errors.New("signing operation failed")
) )
// KeyType represents supported key algorithms // KeyType represents supported key algorithms
type KeyType int type KeyType int
const ( const (
KeyTypeEd25519 KeyType = iota KeyTypeEd25519 KeyType = iota
KeyTypeElgamal KeyTypeElgamal
// Add other key types as needed // Add other key types as needed
) )
// SecretKeyProvider extends the basic crypto interfaces // SecretKeyProvider extends the basic crypto interfaces
type SecretKeyProvider interface { type SecretKeyProvider interface {
crypto.Signer crypto.Signer
Type() KeyType Type() KeyType
Raw() []byte Raw() []byte
} }
// Ed25519SecretKey provides a type-safe wrapper // Ed25519SecretKey provides a type-safe wrapper
type Ed25519SecretKey struct { type Ed25519SecretKey struct {
key ed25519.PrivateKey key ed25519.PrivateKey
} }
func NewEd25519SecretKey(key ed25519.PrivateKey) (*Ed25519SecretKey, error) { func NewEd25519SecretKey(key ed25519.PrivateKey) (*Ed25519SecretKey, error) {
if len(key) != ed25519.PrivateKeySize { if len(key) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("%w: invalid Ed25519 key size", ErrInvalidKeyType) return nil, fmt.Errorf("%w: invalid Ed25519 key size", ErrInvalidKeyType)
} }
return &Ed25519SecretKey{key: key}, nil return &Ed25519SecretKey{key: key}, nil
} }
func (k *Ed25519SecretKey) Type() KeyType { func (k *Ed25519SecretKey) Type() KeyType {
return KeyTypeEd25519 return KeyTypeEd25519
} }
func (k *Ed25519SecretKey) Raw() []byte { func (k *Ed25519SecretKey) Raw() []byte {
return k.key return k.key
} }
func (k *Ed25519SecretKey) Public() crypto.PublicKey { func (k *Ed25519SecretKey) Public() crypto.PublicKey {
return k.key.Public() return k.key.Public()
} }
func (k *Ed25519SecretKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { func (k *Ed25519SecretKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
if k == nil || len(k.key) != ed25519.PrivateKeySize { if k == nil || len(k.key) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("%w: invalid key state", ErrInvalidKeyType) return nil, fmt.Errorf("%w: invalid key state", ErrInvalidKeyType)
} }
return k.key.Sign(rand, digest, opts) return k.key.Sign(rand, digest, opts)
} }

View File

@ -12,62 +12,62 @@ import (
// SecretKey returns a type-safe secret key implementation // SecretKey returns a type-safe secret key implementation
func (k I2PKeys) SecretKey() (SecretKeyProvider, error) { func (k I2PKeys) SecretKey() (SecretKeyProvider, error) {
rawKey := k.Private() rawKey := k.Private()
if len(rawKey) != ed25519.PrivateKeySize { if len(rawKey) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("%w: expected Ed25519 key", ErrInvalidKeyType) return nil, fmt.Errorf("%w: expected Ed25519 key", ErrInvalidKeyType)
} }
return NewEd25519SecretKey(ed25519.PrivateKey(rawKey)) return NewEd25519SecretKey(ed25519.PrivateKey(rawKey))
} }
// PrivateKey returns the crypto.PrivateKey interface implementation // PrivateKey returns the crypto.PrivateKey interface implementation
func (k I2PKeys) PrivateKey() (crypto.PrivateKey, error) { func (k I2PKeys) PrivateKey() (crypto.PrivateKey, error) {
sk, err := k.SecretKey() sk, err := k.SecretKey()
if err != nil { if err != nil {
return nil, fmt.Errorf("getting secret key: %w", err) return nil, fmt.Errorf("getting secret key: %w", err)
} }
return sk, nil return sk, nil
} }
// Ed25519PrivateKey safely converts to ed25519.PrivateKey // Ed25519PrivateKey safely converts to ed25519.PrivateKey
func (k I2PKeys) Ed25519PrivateKey() (ed25519.PrivateKey, error) { func (k I2PKeys) Ed25519PrivateKey() (ed25519.PrivateKey, error) {
sk, err := k.SecretKey() sk, err := k.SecretKey()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sk.Type() != KeyTypeEd25519 { if sk.Type() != KeyTypeEd25519 {
return nil, fmt.Errorf("%w: not an Ed25519 key", ErrInvalidKeyType) return nil, fmt.Errorf("%w: not an Ed25519 key", ErrInvalidKeyType)
} }
return ed25519.PrivateKey(sk.Raw()), nil return ed25519.PrivateKey(sk.Raw()), nil
} }
// Sign implements crypto.Signer // Sign implements crypto.Signer
func (k I2PKeys) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { func (k I2PKeys) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
sk, err := k.SecretKey() sk, err := k.SecretKey()
if err != nil { if err != nil {
return nil, fmt.Errorf("getting secret key: %w", err) return nil, fmt.Errorf("getting secret key: %w", err)
} }
sig, err := sk.Sign(rand, digest, opts) sig, err := sk.Sign(rand, digest, opts)
if err != nil { if err != nil {
return nil, fmt.Errorf("%w: %v", ErrSigningFailed, err) return nil, fmt.Errorf("%w: %v", ErrSigningFailed, err)
} }
return sig, nil return sig, nil
} }
// HostnameEntry creates a signed hostname entry // HostnameEntry creates a signed hostname entry
func (k I2PKeys) HostnameEntry(hostname string, opts crypto.SignerOpts) (string, error) { func (k I2PKeys) HostnameEntry(hostname string, opts crypto.SignerOpts) (string, error) {
if hostname == "" { if hostname == "" {
return "", errors.New("empty hostname") return "", errors.New("empty hostname")
} }
sig, err := k.Sign(rand.Reader, []byte(hostname), opts) sig, err := k.Sign(rand.Reader, []byte(hostname), opts)
if err != nil { if err != nil {
return "", fmt.Errorf("signing hostname: %w", err) return "", fmt.Errorf("signing hostname: %w", err)
} }
return string(sig), nil return string(sig), nil
} }

View File

@ -7,55 +7,55 @@ import (
) )
func TestSecretKeyOperations(t *testing.T) { func TestSecretKeyOperations(t *testing.T) {
// Generate test keys // Generate test keys
pub, priv, err := ed25519.GenerateKey(rand.Reader) pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil { if err != nil {
t.Fatalf("Failed to generate test keys: %v", err) t.Fatalf("Failed to generate test keys: %v", err)
} }
keys := I2PKeys{ keys := I2PKeys{
Address: I2PAddr(pub), Address: I2PAddr(pub),
Both: string(priv), Both: string(priv),
} }
t.Log(len(pub)) t.Log(len(pub))
t.Log(len(keys.Address)) t.Log(len(keys.Address))
t.Log(pub, keys.Address) t.Log(pub, keys.Address)
t.Log(len(priv)) t.Log(len(priv))
t.Log(len(keys.Both)) t.Log(len(keys.Both))
t.Log(priv, keys.Both) t.Log(priv, keys.Both)
/*t.Run("SecretKey", func(t *testing.T) { /*t.Run("SecretKey", func(t *testing.T) {
sk, err := keys.SecretKey() sk, err := keys.SecretKey()
if err != nil { if err != nil {
t.Fatalf("SecretKey() error = %v", err) t.Fatalf("SecretKey() error = %v", err)
} }
if sk.Type() != KeyTypeEd25519 { if sk.Type() != KeyTypeEd25519 {
t.Errorf("Wrong key type, got %v, want %v", sk.Type(), KeyTypeEd25519) t.Errorf("Wrong key type, got %v, want %v", sk.Type(), KeyTypeEd25519)
} }
}) })
t.Run("Sign", func(t *testing.T) { t.Run("Sign", func(t *testing.T) {
message := []byte("test message") message := []byte("test message")
sig, err := keys.Sign(rand.Reader, message, crypto.Hash(0)) sig, err := keys.Sign(rand.Reader, message, crypto.Hash(0))
if err != nil { if err != nil {
t.Fatalf("Sign() error = %v", err) t.Fatalf("Sign() error = %v", err)
} }
if !ed25519.Verify(pub, message, sig) { if !ed25519.Verify(pub, message, sig) {
t.Error("Signature verification failed") t.Error("Signature verification failed")
} }
}) })
t.Run("HostnameEntry", func(t *testing.T) { t.Run("HostnameEntry", func(t *testing.T) {
hostname := "test.i2p" hostname := "test.i2p"
entry, err := keys.HostnameEntry(hostname, crypto.Hash(0)) entry, err := keys.HostnameEntry(hostname, crypto.Hash(0))
if err != nil { if err != nil {
t.Fatalf("HostnameEntry() error = %v", err) t.Fatalf("HostnameEntry() error = %v", err)
} }
if entry == "" { if entry == "" {
t.Error("Empty hostname entry") t.Error("Empty hostname entry")
} }
})*/ })*/
} }

View File

@ -16,7 +16,7 @@ const (
maxResponseSize = 4096 maxResponseSize = 4096
cmdHello = "HELLO VERSION MIN=3.1 MAX=3.1\n" cmdHello = "HELLO VERSION MIN=3.1 MAX=3.1\n"
cmdGenerate = "DEST GENERATE SIGNATURE_TYPE=7\n" cmdGenerate = "DEST GENERATE SIGNATURE_TYPE=%s\n"
responseOK = "RESULT=OK" responseOK = "RESULT=OK"
pubKeyPrefix = "PUB=" pubKeyPrefix = "PUB="
privKeyPrefix = "PRIV=" privKeyPrefix = "PRIV="
@ -44,16 +44,19 @@ func newSAMClient(options ...func(*samClient)) *samClient {
// NewDestination generates a new I2P destination using the SAM bridge. // NewDestination generates a new I2P destination using the SAM bridge.
// This is the only public function that external code should use. // This is the only public function that external code should use.
func NewDestination() (*I2PKeys, error) { func NewDestination(keyType ...string) (*I2PKeys, error) {
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel() defer cancel()
if keyType == nil {
keyType = []string{"7"}
}
client := newSAMClient() client := newSAMClient()
return client.generateDestination(ctx) return client.generateDestination(ctx, keyType[0])
} }
// generateDestination handles the key generation process // generateDestination handles the key generation process
func (c *samClient) generateDestination(ctx context.Context) (*I2PKeys, error) { func (c *samClient) generateDestination(ctx context.Context, keyType string) (*I2PKeys, error) {
conn, err := c.dial(ctx) conn, err := c.dial(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("connecting to SAM bridge: %w", err) return nil, fmt.Errorf("connecting to SAM bridge: %w", err)
@ -64,7 +67,7 @@ func (c *samClient) generateDestination(ctx context.Context) (*I2PKeys, error) {
return nil, fmt.Errorf("SAM handshake failed: %w", err) return nil, fmt.Errorf("SAM handshake failed: %w", err)
} }
keys, err := c.generateKeys(ctx, conn) keys, err := c.generateKeys(ctx, conn, keyType)
if err != nil { if err != nil {
return nil, fmt.Errorf("generating keys: %w", err) return nil, fmt.Errorf("generating keys: %w", err)
} }
@ -98,7 +101,8 @@ func (c *samClient) handshake(ctx context.Context, conn net.Conn) error {
return nil return nil
} }
func (c *samClient) generateKeys(ctx context.Context, conn net.Conn) (*I2PKeys, error) { func (c *samClient) generateKeys(ctx context.Context, conn net.Conn, keyType string) (*I2PKeys, error) {
cmdGenerate := fmt.Sprintf(cmdGenerate, keyType)
if err := c.writeCommand(conn, cmdGenerate); err != nil { if err := c.writeCommand(conn, cmdGenerate); err != nil {
return nil, err return nil, err
} }