mirror of
https://github.com/go-i2p/i2pkeys.git
synced 2025-06-08 01:09:12 -04:00
Add ability to pass different flags to the DEST GENERATE function
This commit is contained in:
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
})*/
|
})*/
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user