diff --git a/I2PAddr.go b/I2PAddr.go index 7cd05c5..9f4ff43 100644 --- a/I2PAddr.go +++ b/I2PAddr.go @@ -13,7 +13,6 @@ const ( // Domain suffixes I2PDomainSuffix = ".i2p" - B32DomainSuffix = ".b32.i2p" ) // I2PAddr represents an I2P destination, equivalent to an IP address. @@ -66,8 +65,8 @@ func validateAddressFormat(addr string) error { len(addr), MinAddressLength, MaxAddressLength) } - if strings.HasSuffix(addr, B32DomainSuffix) { - return fmt.Errorf("cannot convert %s to full destination", B32DomainSuffix) + if strings.HasSuffix(addr, B32Suffix) { + return fmt.Errorf("cannot convert %s to full destination", B32Suffix) } return nil diff --git a/I2PKeyTypes.go b/I2PKeyTypes.go index 3d4aeb4..3f1a75e 100644 --- a/I2PKeyTypes.go +++ b/I2PKeyTypes.go @@ -10,53 +10,53 @@ import ( ) var ( - ErrInvalidKeyType = errors.New("invalid key type") - ErrSigningFailed = errors.New("signing operation failed") + ErrInvalidKeyType = errors.New("invalid key type") + ErrSigningFailed = errors.New("signing operation failed") ) // KeyType represents supported key algorithms type KeyType int const ( - KeyTypeEd25519 KeyType = iota - KeyTypeElgamal - // Add other key types as needed + KeyTypeEd25519 KeyType = iota + KeyTypeElgamal + // Add other key types as needed ) // SecretKeyProvider extends the basic crypto interfaces type SecretKeyProvider interface { - crypto.Signer - Type() KeyType - Raw() []byte + crypto.Signer + Type() KeyType + Raw() []byte } // Ed25519SecretKey provides a type-safe wrapper type Ed25519SecretKey struct { - key ed25519.PrivateKey + key ed25519.PrivateKey } func NewEd25519SecretKey(key ed25519.PrivateKey) (*Ed25519SecretKey, error) { - if len(key) != ed25519.PrivateKeySize { - return nil, fmt.Errorf("%w: invalid Ed25519 key size", ErrInvalidKeyType) - } - return &Ed25519SecretKey{key: key}, nil + if len(key) != ed25519.PrivateKeySize { + return nil, fmt.Errorf("%w: invalid Ed25519 key size", ErrInvalidKeyType) + } + return &Ed25519SecretKey{key: key}, nil } func (k *Ed25519SecretKey) Type() KeyType { - return KeyTypeEd25519 + return KeyTypeEd25519 } func (k *Ed25519SecretKey) Raw() []byte { - return k.key + return k.key } 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) { - if k == nil || len(k.key) != ed25519.PrivateKeySize { - return nil, fmt.Errorf("%w: invalid key state", ErrInvalidKeyType) - } - return k.key.Sign(rand, digest, opts) -} \ No newline at end of file + if k == nil || len(k.key) != ed25519.PrivateKeySize { + return nil, fmt.Errorf("%w: invalid key state", ErrInvalidKeyType) + } + return k.key.Sign(rand, digest, opts) +} diff --git a/I2PSecretKey.go b/I2PSecretKey.go index 906a754..5e191cb 100644 --- a/I2PSecretKey.go +++ b/I2PSecretKey.go @@ -12,62 +12,62 @@ import ( // SecretKey returns a type-safe secret key implementation func (k I2PKeys) SecretKey() (SecretKeyProvider, error) { - rawKey := k.Private() - if len(rawKey) != ed25519.PrivateKeySize { - return nil, fmt.Errorf("%w: expected Ed25519 key", ErrInvalidKeyType) - } - - return NewEd25519SecretKey(ed25519.PrivateKey(rawKey)) + rawKey := k.Private() + if len(rawKey) != ed25519.PrivateKeySize { + return nil, fmt.Errorf("%w: expected Ed25519 key", ErrInvalidKeyType) + } + + return NewEd25519SecretKey(ed25519.PrivateKey(rawKey)) } // PrivateKey returns the crypto.PrivateKey interface implementation func (k I2PKeys) PrivateKey() (crypto.PrivateKey, error) { - sk, err := k.SecretKey() - if err != nil { - return nil, fmt.Errorf("getting secret key: %w", err) - } - return sk, nil + sk, err := k.SecretKey() + if err != nil { + return nil, fmt.Errorf("getting secret key: %w", err) + } + return sk, nil } // Ed25519PrivateKey safely converts to ed25519.PrivateKey func (k I2PKeys) Ed25519PrivateKey() (ed25519.PrivateKey, error) { - sk, err := k.SecretKey() - if err != nil { - return nil, err - } - - if sk.Type() != KeyTypeEd25519 { - return nil, fmt.Errorf("%w: not an Ed25519 key", ErrInvalidKeyType) - } - - return ed25519.PrivateKey(sk.Raw()), nil + sk, err := k.SecretKey() + if err != nil { + return nil, err + } + + if sk.Type() != KeyTypeEd25519 { + return nil, fmt.Errorf("%w: not an Ed25519 key", ErrInvalidKeyType) + } + + return ed25519.PrivateKey(sk.Raw()), nil } // Sign implements crypto.Signer func (k I2PKeys) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - sk, err := k.SecretKey() - if err != nil { - return nil, fmt.Errorf("getting secret key: %w", err) - } - - sig, err := sk.Sign(rand, digest, opts) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrSigningFailed, err) - } - - return sig, nil + sk, err := k.SecretKey() + if err != nil { + return nil, fmt.Errorf("getting secret key: %w", err) + } + + sig, err := sk.Sign(rand, digest, opts) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrSigningFailed, err) + } + + return sig, nil } // HostnameEntry creates a signed hostname entry func (k I2PKeys) HostnameEntry(hostname string, opts crypto.SignerOpts) (string, error) { - if hostname == "" { - return "", errors.New("empty hostname") - } - - sig, err := k.Sign(rand.Reader, []byte(hostname), opts) - if err != nil { - return "", fmt.Errorf("signing hostname: %w", err) - } - - return string(sig), nil -} \ No newline at end of file + if hostname == "" { + return "", errors.New("empty hostname") + } + + sig, err := k.Sign(rand.Reader, []byte(hostname), opts) + if err != nil { + return "", fmt.Errorf("signing hostname: %w", err) + } + + return string(sig), nil +} diff --git a/I2PSecretKeys_test.go b/I2PSecretKeys_test.go index a429c5b..56e4ce1 100644 --- a/I2PSecretKeys_test.go +++ b/I2PSecretKeys_test.go @@ -7,55 +7,55 @@ import ( ) func TestSecretKeyOperations(t *testing.T) { - // Generate test keys - pub, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("Failed to generate test keys: %v", err) - } - - keys := I2PKeys{ - Address: I2PAddr(pub), - Both: string(priv), - } - t.Log(len(pub)) - t.Log(len(keys.Address)) - t.Log(pub, keys.Address) - t.Log(len(priv)) - t.Log(len(keys.Both)) - t.Log(priv, keys.Both) - - /*t.Run("SecretKey", func(t *testing.T) { - sk, err := keys.SecretKey() - if err != nil { - t.Fatalf("SecretKey() error = %v", err) - } - - if sk.Type() != KeyTypeEd25519 { - t.Errorf("Wrong key type, got %v, want %v", sk.Type(), KeyTypeEd25519) - } - }) - - t.Run("Sign", func(t *testing.T) { - message := []byte("test message") - sig, err := keys.Sign(rand.Reader, message, crypto.Hash(0)) - if err != nil { - t.Fatalf("Sign() error = %v", err) - } - - if !ed25519.Verify(pub, message, sig) { - t.Error("Signature verification failed") - } - }) - - t.Run("HostnameEntry", func(t *testing.T) { - hostname := "test.i2p" - entry, err := keys.HostnameEntry(hostname, crypto.Hash(0)) - if err != nil { - t.Fatalf("HostnameEntry() error = %v", err) - } - - if entry == "" { - t.Error("Empty hostname entry") - } - })*/ -} \ No newline at end of file + // Generate test keys + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + t.Fatalf("Failed to generate test keys: %v", err) + } + + keys := I2PKeys{ + Address: I2PAddr(pub), + Both: string(priv), + } + t.Log(len(pub)) + t.Log(len(keys.Address)) + t.Log(pub, keys.Address) + t.Log(len(priv)) + t.Log(len(keys.Both)) + t.Log(priv, keys.Both) + + /*t.Run("SecretKey", func(t *testing.T) { + sk, err := keys.SecretKey() + if err != nil { + t.Fatalf("SecretKey() error = %v", err) + } + + if sk.Type() != KeyTypeEd25519 { + t.Errorf("Wrong key type, got %v, want %v", sk.Type(), KeyTypeEd25519) + } + }) + + t.Run("Sign", func(t *testing.T) { + message := []byte("test message") + sig, err := keys.Sign(rand.Reader, message, crypto.Hash(0)) + if err != nil { + t.Fatalf("Sign() error = %v", err) + } + + if !ed25519.Verify(pub, message, sig) { + t.Error("Signature verification failed") + } + }) + + t.Run("HostnameEntry", func(t *testing.T) { + hostname := "test.i2p" + entry, err := keys.HostnameEntry(hostname, crypto.Hash(0)) + if err != nil { + t.Fatalf("HostnameEntry() error = %v", err) + } + + if entry == "" { + t.Error("Empty hostname entry") + } + })*/ +} diff --git a/NewI2PKeys.go b/NewI2PKeys.go index 76f966c..0fb4c58 100644 --- a/NewI2PKeys.go +++ b/NewI2PKeys.go @@ -16,7 +16,7 @@ const ( maxResponseSize = 4096 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" pubKeyPrefix = "PUB=" privKeyPrefix = "PRIV=" @@ -44,16 +44,19 @@ func newSAMClient(options ...func(*samClient)) *samClient { // NewDestination generates a new I2P destination using the SAM bridge. // 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) defer cancel() + if keyType == nil { + keyType = []string{"7"} + } client := newSAMClient() - return client.generateDestination(ctx) + return client.generateDestination(ctx, keyType[0]) } // 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) if err != nil { 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) } - keys, err := c.generateKeys(ctx, conn) + keys, err := c.generateKeys(ctx, conn, keyType) if err != nil { 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 } -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 { return nil, err }