mirror of
https://github.com/go-i2p/go-i2cp.git
synced 2025-12-20 15:15:53 -05:00
Migrate to go-i2p/crypto package for cryptographic operations
This commit is contained in:
@@ -1,26 +1,37 @@
|
||||
// Migration Notes (November 24, 2025):
|
||||
// Migrated to use github.com/go-i2p/crypto/chacha20poly1305 package which provides
|
||||
// standardized ChaCha20-Poly1305 AEAD (Authenticated Encryption with Associated Data).
|
||||
//
|
||||
// Key Changes:
|
||||
// - ChaCha20Poly1305Cipher now wraps crypto/chacha20poly1305.AEAD
|
||||
// - NewChaCha20Poly1305Cipher() delegates to chacha20poly1305.GenerateKey() and NewAEAD()
|
||||
// - Encrypt/Decrypt maintain [nonce][ciphertext+tag] format for backward compatibility
|
||||
// - Crypto package separates tag from ciphertext; wrapper combines them for I2CP compatibility
|
||||
//
|
||||
// The crypto package uses explicit tag separation (AEAD standard), while I2CP traditionally
|
||||
// combines nonce+ciphertext+tag. The wrapper maintains I2CP format compatibility.
|
||||
|
||||
package go_i2cp
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
cryptoaead "github.com/go-i2p/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// ChaCha20Poly1305Cipher provides authenticated encryption using ChaCha20-Poly1305
|
||||
// Wraps github.com/go-i2p/crypto/chacha20poly1305.AEAD
|
||||
type ChaCha20Poly1305Cipher struct {
|
||||
algorithmType uint32
|
||||
aead cipher.AEAD
|
||||
aead *cryptoaead.AEAD
|
||||
key [32]byte
|
||||
}
|
||||
|
||||
// NewChaCha20Poly1305Cipher creates a new ChaCha20-Poly1305 cipher with a random key
|
||||
// Delegates to github.com/go-i2p/crypto/chacha20poly1305.GenerateKey() and NewAEAD()
|
||||
func NewChaCha20Poly1305Cipher() (*ChaCha20Poly1305Cipher, error) {
|
||||
var key [32]byte
|
||||
|
||||
// Generate random 256-bit key
|
||||
_, err := rand.Read(key[:])
|
||||
// Generate random 256-bit key using crypto package
|
||||
key, err := cryptoaead.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate ChaCha20-Poly1305 key: %w", err)
|
||||
}
|
||||
@@ -29,8 +40,9 @@ func NewChaCha20Poly1305Cipher() (*ChaCha20Poly1305Cipher, error) {
|
||||
}
|
||||
|
||||
// NewChaCha20Poly1305CipherWithKey creates a new ChaCha20-Poly1305 cipher with the provided key
|
||||
// Delegates to github.com/go-i2p/crypto/chacha20poly1305.NewAEAD()
|
||||
func NewChaCha20Poly1305CipherWithKey(key [32]byte) (*ChaCha20Poly1305Cipher, error) {
|
||||
aead, err := chacha20poly1305.New(key[:])
|
||||
aead, err := cryptoaead.NewAEAD(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create ChaCha20-Poly1305 AEAD: %w", err)
|
||||
}
|
||||
@@ -66,25 +78,31 @@ func ChaCha20Poly1305CipherFromStream(stream *Stream) (*ChaCha20Poly1305Cipher,
|
||||
}
|
||||
|
||||
// Encrypt encrypts plaintext with optional associated data using ChaCha20-Poly1305
|
||||
// Returns [nonce][ciphertext+tag] format for I2CP compatibility
|
||||
// Uses github.com/go-i2p/crypto/chacha20poly1305.AEAD.Encrypt()
|
||||
func (c *ChaCha20Poly1305Cipher) Encrypt(plaintext, additionalData []byte) ([]byte, error) {
|
||||
if c.aead == nil {
|
||||
return nil, fmt.Errorf("cipher not initialized")
|
||||
}
|
||||
|
||||
// Generate random nonce
|
||||
nonce := make([]byte, c.aead.NonceSize())
|
||||
_, err := rand.Read(nonce)
|
||||
// Generate random nonce using crypto package
|
||||
nonce, err := cryptoaead.GenerateNonce()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate nonce: %w", err)
|
||||
}
|
||||
|
||||
// Encrypt plaintext
|
||||
ciphertext := c.aead.Seal(nil, nonce, plaintext, additionalData)
|
||||
// Encrypt plaintext - crypto package returns separate ciphertext and tag
|
||||
ciphertext, tag, err := c.aead.Encrypt(plaintext, additionalData, nonce[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt: %w", err)
|
||||
}
|
||||
|
||||
// Prepend nonce to ciphertext for transmission
|
||||
result := make([]byte, len(nonce)+len(ciphertext))
|
||||
copy(result, nonce)
|
||||
// Combine nonce, ciphertext, and tag for I2CP compatibility
|
||||
// Format: [nonce][ciphertext][tag]
|
||||
result := make([]byte, len(nonce)+len(ciphertext)+len(tag))
|
||||
copy(result, nonce[:])
|
||||
copy(result[len(nonce):], ciphertext)
|
||||
copy(result[len(nonce)+len(ciphertext):], tag[:])
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -110,22 +128,32 @@ func (c *ChaCha20Poly1305Cipher) EncryptStream(src, dst *Stream, additionalData
|
||||
}
|
||||
|
||||
// Decrypt decrypts ciphertext with optional associated data using ChaCha20-Poly1305
|
||||
// Expects [nonce][ciphertext+tag] format for I2CP compatibility
|
||||
// Uses github.com/go-i2p/crypto/chacha20poly1305.AEAD.Decrypt()
|
||||
func (c *ChaCha20Poly1305Cipher) Decrypt(ciphertext, additionalData []byte) ([]byte, error) {
|
||||
if c.aead == nil {
|
||||
return nil, fmt.Errorf("cipher not initialized")
|
||||
}
|
||||
|
||||
nonceSize := c.aead.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return nil, fmt.Errorf("ciphertext too short: need at least %d bytes for nonce", nonceSize)
|
||||
nonceSize := cryptoaead.NonceSize
|
||||
tagSize := cryptoaead.TagSize
|
||||
minSize := nonceSize + tagSize
|
||||
|
||||
if len(ciphertext) < minSize {
|
||||
return nil, fmt.Errorf("ciphertext too short: need at least %d bytes (nonce + tag)", minSize)
|
||||
}
|
||||
|
||||
// Extract nonce and actual ciphertext
|
||||
// Extract nonce, ciphertext, and tag from combined format
|
||||
nonce := ciphertext[:nonceSize]
|
||||
actualCiphertext := ciphertext[nonceSize:]
|
||||
actualCiphertextWithTag := ciphertext[nonceSize:]
|
||||
|
||||
// Decrypt ciphertext
|
||||
plaintext, err := c.aead.Open(nil, nonce, actualCiphertext, additionalData)
|
||||
// Split ciphertext and tag
|
||||
actualCiphertext := actualCiphertextWithTag[:len(actualCiphertextWithTag)-tagSize]
|
||||
var tag [cryptoaead.TagSize]byte
|
||||
copy(tag[:], actualCiphertextWithTag[len(actualCiphertext):])
|
||||
|
||||
// Decrypt using crypto package - pass tag separately
|
||||
plaintext, err := c.aead.Decrypt(actualCiphertext, tag[:], additionalData, nonce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt: %w", err)
|
||||
}
|
||||
@@ -186,16 +214,10 @@ func (c *ChaCha20Poly1305Cipher) AlgorithmType() uint32 {
|
||||
|
||||
// NonceSize returns the nonce size used by the cipher
|
||||
func (c *ChaCha20Poly1305Cipher) NonceSize() int {
|
||||
if c.aead == nil {
|
||||
return chacha20poly1305.NonceSize // Return standard size even if not initialized
|
||||
}
|
||||
return c.aead.NonceSize()
|
||||
return cryptoaead.NonceSize
|
||||
}
|
||||
|
||||
// Overhead returns the authentication tag overhead
|
||||
func (c *ChaCha20Poly1305Cipher) Overhead() int {
|
||||
if c.aead == nil {
|
||||
return chacha20poly1305.Overhead // Return standard overhead even if not initialized
|
||||
}
|
||||
return c.aead.Overhead()
|
||||
return cryptoaead.TagSize
|
||||
}
|
||||
|
||||
80
crypto.go
80
crypto.go
@@ -28,6 +28,17 @@ func NewCrypto() *Crypto {
|
||||
|
||||
// Sign a stream using the specified algorithm
|
||||
func (c *Crypto) SignStream(sgk *SignatureKeyPair, stream *Stream) (err error) {
|
||||
// Use new DSA wrapper if available
|
||||
if sgk.dsaKeyPair != nil {
|
||||
signature, err := sgk.dsaKeyPair.Sign(stream.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sign stream with DSA: %w", err)
|
||||
}
|
||||
stream.Write(signature)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback to legacy implementation for backward compatibility
|
||||
var r, s *big.Int
|
||||
out := NewStream(make([]byte, 40))
|
||||
c.sh1.Reset()
|
||||
@@ -76,12 +87,21 @@ func (c *Crypto) VerifyStream(sgk *SignatureKeyPair, stream *Stream) (verified b
|
||||
Fatal(tAG|FATAL, "Stream length < 40 bytes (signature length)")
|
||||
return false, fmt.Errorf("stream too short for signature verification")
|
||||
}
|
||||
var r, s big.Int
|
||||
|
||||
message := stream.Bytes()[:stream.Len()-40]
|
||||
digest := stream.Bytes()[stream.Len()-40:]
|
||||
signature := stream.Bytes()[stream.Len()-40:]
|
||||
|
||||
// Use new DSA wrapper if available
|
||||
if sgk.dsaKeyPair != nil {
|
||||
verified = sgk.dsaKeyPair.Verify(message, signature)
|
||||
return verified, nil
|
||||
}
|
||||
|
||||
// Fallback to legacy implementation for backward compatibility
|
||||
var r, s big.Int
|
||||
// TODO not sure about this part...
|
||||
r.SetBytes(digest[:20])
|
||||
s.SetBytes(digest[20:])
|
||||
r.SetBytes(signature[:20])
|
||||
s.SetBytes(signature[20:])
|
||||
verified = dsa.Verify(&sgk.pub, message, &r, &s)
|
||||
return
|
||||
}
|
||||
@@ -190,24 +210,43 @@ func (c *Crypto) PublicKeyFromStream(keyType uint32, stream *Stream) (key *big.I
|
||||
func (c *Crypto) SignatureKeygen(algorithmTyp uint32) (sgk SignatureKeyPair, err error) {
|
||||
switch algorithmTyp {
|
||||
case DSA_SHA1:
|
||||
var pkey dsa.PrivateKey
|
||||
pkey.G = c.params.G
|
||||
pkey.Q = c.params.Q
|
||||
pkey.P = c.params.P
|
||||
err = dsa.GenerateKey(&pkey, c.rng)
|
||||
sgk.priv = pkey
|
||||
sgk.pub.G = pkey.G
|
||||
sgk.pub.P = pkey.P
|
||||
sgk.pub.Q = pkey.Q
|
||||
sgk.pub.Y = pkey.Y
|
||||
// Use new DSA wrapper from crypto package
|
||||
dsaKp, err := c.DSASignatureKeygen()
|
||||
if err != nil {
|
||||
return sgk, fmt.Errorf("failed to generate DSA key pair: %w", err)
|
||||
}
|
||||
|
||||
// Convert new DSAKeyPair to legacy SignatureKeyPair for backward compatibility
|
||||
// This allows existing code to continue working while we migrate to the new types
|
||||
sgk.algorithmType = DSA_SHA1
|
||||
sgk.dsaKeyPair = dsaKp
|
||||
|
||||
// Also populate legacy fields for backward compatibility
|
||||
// Extract raw bytes and reconstruct old big.Int format
|
||||
privKeyBytes := dsaKp.PrivateKey()
|
||||
pubKeyBytes := dsaKp.PublicKey()
|
||||
|
||||
// Initialize DSA parameters from crypto struct
|
||||
sgk.priv.G = c.params.G
|
||||
sgk.priv.Q = c.params.Q
|
||||
sgk.priv.P = c.params.P
|
||||
sgk.pub.G = c.params.G
|
||||
sgk.pub.Q = c.params.Q
|
||||
sgk.pub.P = c.params.P
|
||||
|
||||
// Set private key X from bytes
|
||||
sgk.priv.X = new(big.Int)
|
||||
sgk.priv.X.SetBytes(privKeyBytes)
|
||||
|
||||
// Set public key Y from bytes
|
||||
sgk.pub.Y = new(big.Int)
|
||||
sgk.pub.Y.SetBytes(pubKeyBytes)
|
||||
sgk.priv.Y = sgk.pub.Y // Private key also stores public key Y
|
||||
default:
|
||||
err = fmt.Errorf("unsupported signature algorithm type: %d", algorithmTyp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Ed25519SignatureKeygen generates a new Ed25519 signature key pair
|
||||
} // Ed25519SignatureKeygen generates a new Ed25519 signature key pair
|
||||
func (c *Crypto) Ed25519SignatureKeygen() (*Ed25519KeyPair, error) {
|
||||
return NewEd25519KeyPair()
|
||||
}
|
||||
@@ -222,7 +261,12 @@ func (c *Crypto) ChaCha20Poly1305CipherKeygen() (*ChaCha20Poly1305Cipher, error)
|
||||
return NewChaCha20Poly1305Cipher()
|
||||
}
|
||||
|
||||
// Random32 generates a cryptographically secure random uint32 value
|
||||
// DSASignatureKeygen generates a new DSA signature key pair
|
||||
func (c *Crypto) DSASignatureKeygen() (*DSAKeyPair, error) {
|
||||
return NewDSAKeyPair()
|
||||
}
|
||||
|
||||
// Random32 generates a cryptographically secure random uint32.
|
||||
// Used for I2CP message nonces and request IDs per protocol specification
|
||||
func (c *Crypto) Random32() uint32 {
|
||||
var bytes [4]byte
|
||||
|
||||
141
dsa.go
Normal file
141
dsa.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package go_i2cp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cryptodsa "github.com/go-i2p/crypto/dsa"
|
||||
)
|
||||
|
||||
// DSAKeyPair represents a DSA signature key pair using the crypto package
|
||||
type DSAKeyPair struct {
|
||||
privKey cryptodsa.DSAPrivateKey
|
||||
pubKey cryptodsa.DSAPublicKey
|
||||
}
|
||||
|
||||
// NewDSAKeyPair generates a new DSA key pair using the crypto package
|
||||
func NewDSAKeyPair() (*DSAKeyPair, error) {
|
||||
var privKey cryptodsa.DSAPrivateKey
|
||||
generatedKey, err := privKey.Generate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate DSA key pair: %w", err)
|
||||
}
|
||||
|
||||
privKey = generatedKey.(cryptodsa.DSAPrivateKey)
|
||||
|
||||
pubKeyInterface, err := privKey.Public()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive public key: %w", err)
|
||||
}
|
||||
|
||||
pubKey := pubKeyInterface.(cryptodsa.DSAPublicKey)
|
||||
|
||||
return &DSAKeyPair{
|
||||
privKey: privKey,
|
||||
pubKey: pubKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AlgorithmType returns DSA_SHA1 algorithm type
|
||||
func (kp *DSAKeyPair) AlgorithmType() uint32 {
|
||||
return DSA_SHA1
|
||||
}
|
||||
|
||||
// Sign creates a DSA signature for the given data
|
||||
func (kp *DSAKeyPair) Sign(data []byte) ([]byte, error) {
|
||||
signer, err := kp.privKey.NewSigner()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create signer: %w", err)
|
||||
}
|
||||
|
||||
signature, err := signer.Sign(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("signing failed: %w", err)
|
||||
}
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
// Verify verifies a DSA signature against the given data
|
||||
func (kp *DSAKeyPair) Verify(data, signature []byte) bool {
|
||||
verifier, err := kp.pubKey.NewVerifier()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
err = verifier.Verify(data, signature)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// PrivateKey returns the private key bytes
|
||||
func (kp *DSAKeyPair) PrivateKey() []byte {
|
||||
return kp.privKey.Bytes()
|
||||
}
|
||||
|
||||
// PublicKey returns the public key bytes
|
||||
func (kp *DSAKeyPair) PublicKey() []byte {
|
||||
return kp.pubKey.Bytes()
|
||||
}
|
||||
|
||||
// WriteToStream writes the DSA key pair to a stream in I2CP format
|
||||
func (kp *DSAKeyPair) WriteToStream(stream *Stream) error {
|
||||
// Write algorithm type
|
||||
err := stream.WriteUint32(DSA_SHA1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write algorithm type: %w", err)
|
||||
}
|
||||
|
||||
// Write private key (20 bytes)
|
||||
privKeyBytes := kp.privKey.Bytes()
|
||||
if len(privKeyBytes) != 20 {
|
||||
return fmt.Errorf("invalid DSA private key length: %d (expected 20)", len(privKeyBytes))
|
||||
}
|
||||
_, err = stream.Write(privKeyBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write private key: %w", err)
|
||||
}
|
||||
|
||||
// Write public key (128 bytes)
|
||||
pubKeyBytes := kp.pubKey.Bytes()
|
||||
if len(pubKeyBytes) != 128 {
|
||||
return fmt.Errorf("invalid DSA public key length: %d (expected 128)", len(pubKeyBytes))
|
||||
}
|
||||
_, err = stream.Write(pubKeyBytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write public key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DSAKeyPairFromStream reads a DSA key pair from a stream
|
||||
func DSAKeyPairFromStream(stream *Stream) (*DSAKeyPair, error) {
|
||||
// Read private key (20 bytes)
|
||||
privKeyBytes := make([]byte, 20)
|
||||
_, err := stream.Read(privKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read private key: %w", err)
|
||||
}
|
||||
|
||||
// Read public key (128 bytes)
|
||||
pubKeyBytes := make([]byte, 128)
|
||||
_, err = stream.Read(pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read public key: %w", err)
|
||||
}
|
||||
|
||||
// Convert to crypto package types
|
||||
var privKey cryptodsa.DSAPrivateKey
|
||||
copy(privKey[:], privKeyBytes)
|
||||
|
||||
var pubKey cryptodsa.DSAPublicKey
|
||||
copy(pubKey[:], pubKeyBytes)
|
||||
|
||||
return &DSAKeyPair{
|
||||
privKey: privKey,
|
||||
pubKey: pubKey,
|
||||
}, nil
|
||||
}
|
||||
108
ed25519.go
108
ed25519.go
@@ -2,33 +2,60 @@ package go_i2cp
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
cryptoed25519 "github.com/go-i2p/crypto/ed25519"
|
||||
)
|
||||
|
||||
// Ed25519KeyPair represents an Ed25519 signature key pair for modern I2P cryptography
|
||||
// Ed25519 Implementation Migration Notes:
|
||||
//
|
||||
// This file has been migrated to use github.com/go-i2p/crypto/ed25519 (v0.0.5)
|
||||
// for all cryptographic operations, following Phase 2.1 of the migration plan.
|
||||
//
|
||||
// Migration completed: November 24, 2025
|
||||
//
|
||||
// Key changes:
|
||||
// - Ed25519KeyPair now wraps crypto package types (Ed25519PrivateKey, Ed25519PublicKey)
|
||||
// - Sign() delegates to crypto.Signer interface (NewSigner().SignHash())
|
||||
// - Verify() delegates to crypto.Verifier interface (NewVerifier().VerifyHash())
|
||||
// - NewEd25519KeyPair() uses crypto.GenerateEd25519KeyPair()
|
||||
// - Maintains I2CP-specific SHA-256 pre-hashing for protocol compatibility
|
||||
// - Backward compatible API - all existing tests pass without modification
|
||||
//
|
||||
// The crypto package provides:
|
||||
// - Interface-based signing/verification (types.Signer, types.Verifier)
|
||||
// - Secure key generation and management
|
||||
// - Memory zeroing for private keys (privKey.Zero())
|
||||
// - Consistent cryptographic operations across go-i2p ecosystem
|
||||
|
||||
// Ed25519KeyPair represents an Ed25519 signature key pair for modern I2P cryptography.
|
||||
// This type wraps github.com/go-i2p/crypto/ed25519 keys to provide I2CP-specific
|
||||
// functionality while delegating cryptographic operations to the specialized crypto package.
|
||||
type Ed25519KeyPair struct {
|
||||
algorithmType uint32
|
||||
privateKey ed25519.PrivateKey
|
||||
publicKey ed25519.PublicKey
|
||||
privateKey cryptoed25519.Ed25519PrivateKey
|
||||
publicKey cryptoed25519.Ed25519PublicKey
|
||||
}
|
||||
|
||||
// NewEd25519KeyPair generates a new Ed25519 key pair
|
||||
// NewEd25519KeyPair generates a new Ed25519 key pair using github.com/go-i2p/crypto.
|
||||
// This function delegates to the crypto package for secure key generation.
|
||||
func NewEd25519KeyPair() (*Ed25519KeyPair, error) {
|
||||
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
publicKey, privateKey, err := cryptoed25519.GenerateEd25519KeyPair()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate Ed25519 key pair: %w", err)
|
||||
}
|
||||
|
||||
// Dereference pointers - crypto package keys are slices
|
||||
return &Ed25519KeyPair{
|
||||
algorithmType: ED25519_SHA256,
|
||||
privateKey: privateKey,
|
||||
publicKey: publicKey,
|
||||
privateKey: *privateKey,
|
||||
publicKey: *publicKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Ed25519KeyPairFromStream reads an Ed25519 key pair from a stream
|
||||
// Ed25519KeyPairFromStream reads an Ed25519 key pair from a stream.
|
||||
// Uses github.com/go-i2p/crypto/ed25519 for key reconstruction.
|
||||
func Ed25519KeyPairFromStream(stream *Stream) (*Ed25519KeyPair, error) {
|
||||
var algorithmType uint32
|
||||
var err error
|
||||
@@ -56,14 +83,27 @@ func Ed25519KeyPairFromStream(stream *Stream) (*Ed25519KeyPair, error) {
|
||||
return nil, fmt.Errorf("failed to read public key: %w", err)
|
||||
}
|
||||
|
||||
// Construct crypto package keys from raw bytes
|
||||
privKey, err := cryptoed25519.CreateEd25519PrivateKeyFromBytes(privateKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create private key: %w", err)
|
||||
}
|
||||
|
||||
pubKey, err := cryptoed25519.CreateEd25519PublicKeyFromBytes(publicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create public key: %w", err)
|
||||
}
|
||||
|
||||
// CreateFromBytes returns values, not pointers
|
||||
return &Ed25519KeyPair{
|
||||
algorithmType: algorithmType,
|
||||
privateKey: ed25519.PrivateKey(privateKeyBytes),
|
||||
publicKey: ed25519.PublicKey(publicKeyBytes),
|
||||
privateKey: privKey,
|
||||
publicKey: pubKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Ed25519PublicKeyFromStream reads only the public key from a stream
|
||||
// Ed25519PublicKeyFromStream reads only the public key from a stream.
|
||||
// Returns stdlib ed25519.PublicKey for backward compatibility.
|
||||
func Ed25519PublicKeyFromStream(stream *Stream) (ed25519.PublicKey, error) {
|
||||
publicKeyBytes := make([]byte, ed25519.PublicKeySize)
|
||||
_, err := stream.Read(publicKeyBytes)
|
||||
@@ -73,18 +113,30 @@ func Ed25519PublicKeyFromStream(stream *Stream) (ed25519.PublicKey, error) {
|
||||
return ed25519.PublicKey(publicKeyBytes), nil
|
||||
}
|
||||
|
||||
// Sign creates a signature for the given message using Ed25519
|
||||
// Sign creates a signature for the given message using Ed25519.
|
||||
// Uses github.com/go-i2p/crypto/ed25519 Signer interface for cryptographic operations.
|
||||
func (kp *Ed25519KeyPair) Sign(message []byte) ([]byte, error) {
|
||||
if kp.privateKey == nil {
|
||||
return nil, fmt.Errorf("private key is nil")
|
||||
}
|
||||
|
||||
// Ed25519 signs the message directly, but for I2CP compatibility we hash it first
|
||||
// Create signer from private key
|
||||
signer, err := kp.privateKey.NewSigner()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create signer: %w", err)
|
||||
}
|
||||
|
||||
// For I2CP compatibility, hash the message first using SHA-256
|
||||
hasher := sha256.New()
|
||||
hasher.Write(message)
|
||||
messageHash := hasher.Sum(nil)
|
||||
|
||||
signature := ed25519.Sign(kp.privateKey, messageHash)
|
||||
// Sign the hash using the crypto package's interface-based signing
|
||||
signature, err := signer.SignHash(messageHash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign message: %w", err)
|
||||
}
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
@@ -107,18 +159,27 @@ func (kp *Ed25519KeyPair) SignStream(stream *Stream) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify verifies a signature against the given message using Ed25519
|
||||
// Verify verifies a signature against the given message using Ed25519.
|
||||
// Uses github.com/go-i2p/crypto/ed25519 Verifier interface for cryptographic operations.
|
||||
func (kp *Ed25519KeyPair) Verify(message, signature []byte) bool {
|
||||
if kp.publicKey == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Create verifier from public key
|
||||
verifier, err := kp.publicKey.NewVerifier()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Hash the message for I2CP compatibility
|
||||
hasher := sha256.New()
|
||||
hasher.Write(message)
|
||||
messageHash := hasher.Sum(nil)
|
||||
|
||||
return ed25519.Verify(kp.publicKey, messageHash, signature)
|
||||
// Verify using the crypto package's interface-based verification
|
||||
err = verifier.VerifyHash(messageHash, signature)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// VerifyStream verifies a signature embedded in the stream data
|
||||
@@ -176,13 +237,14 @@ func (kp *Ed25519KeyPair) WriteToStream(stream *Stream) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePublicKeyToStream writes only the public key to a stream
|
||||
// WritePublicKeyToStream writes only the public key to a stream.
|
||||
// Uses crypto package's Bytes() method for key serialization.
|
||||
func (kp *Ed25519KeyPair) WritePublicKeyToStream(stream *Stream) error {
|
||||
if stream == nil {
|
||||
return fmt.Errorf("stream cannot be nil")
|
||||
}
|
||||
|
||||
_, err := stream.Write(kp.publicKey)
|
||||
_, err := stream.Write(kp.publicKey.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write Ed25519 public key: %w", err)
|
||||
}
|
||||
@@ -190,14 +252,14 @@ func (kp *Ed25519KeyPair) WritePublicKeyToStream(stream *Stream) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PublicKey returns the public key
|
||||
// PublicKey returns the public key as stdlib ed25519.PublicKey for backward compatibility.
|
||||
func (kp *Ed25519KeyPair) PublicKey() ed25519.PublicKey {
|
||||
return kp.publicKey
|
||||
return ed25519.PublicKey(kp.publicKey.Bytes())
|
||||
}
|
||||
|
||||
// PrivateKey returns the private key
|
||||
// PrivateKey returns the private key as stdlib ed25519.PrivateKey for backward compatibility.
|
||||
func (kp *Ed25519KeyPair) PrivateKey() ed25519.PrivateKey {
|
||||
return kp.privateKey
|
||||
return ed25519.PrivateKey(kp.privateKey.Bytes())
|
||||
}
|
||||
|
||||
// AlgorithmType returns the algorithm type constant
|
||||
|
||||
12
go.mod
12
go.mod
@@ -3,14 +3,20 @@ module github.com/go-i2p/go-i2cp
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/go-i2p/crypto v0.0.5
|
||||
github.com/go-i2p/logger v0.0.1
|
||||
go.step.sm/crypto v0.67.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
github.com/samber/oops v1.19.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
)
|
||||
|
||||
21
go.sum
21
go.sum
@@ -1,23 +1,44 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-i2p/crypto v0.0.5 h1:i6EiQ4rKnNDDcACFfCkizgfMTwxFcMRj187w/VLiD9o=
|
||||
github.com/go-i2p/crypto v0.0.5/go.mod h1:uerqW6jReQkeNgXXMM3n55wZbKbnvuZrbsDaNL9oLjo=
|
||||
github.com/go-i2p/logger v0.0.1 h1:OFDZMjqiNXbPIm+SDxiwYtI6ocC3mb9V/t5kvZ+6XQ0=
|
||||
github.com/go-i2p/logger v0.0.1/go.mod h1:te7Zj3g3oMeIl8uBXAgO62UKmZ6m6kHRNg1Mm+X8Hzk=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
|
||||
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/oops v1.19.0 h1:sfZAwC8MmTXBRRyNc4Z1utuTPBx+hFKF5fJ9DEQRZfw=
|
||||
github.com/samber/oops v1.19.0/go.mod h1:+f+61dbiMxEMQ8gw/zTxW2pk+YGobaDM4glEHQtPOww=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.step.sm/crypto v0.67.0 h1:1km9LmxMKG/p+mKa1R4luPN04vlJYnRLlLQrWv7egGU=
|
||||
go.step.sm/crypto v0.67.0/go.mod h1:+AoDpB0mZxbW/PmOXuwkPSpXRgaUaoIK+/Wx/HGgtAU=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -7,8 +7,11 @@ import (
|
||||
)
|
||||
|
||||
// SignatureKeyPair represents a DSA signature key pair
|
||||
// This is a legacy struct maintained for backward compatibility.
|
||||
// New code should use DSAKeyPair directly from dsa.go
|
||||
type SignatureKeyPair struct {
|
||||
algorithmType uint32
|
||||
pub dsa.PublicKey
|
||||
priv dsa.PrivateKey
|
||||
dsaKeyPair *DSAKeyPair // New wrapper from crypto package
|
||||
}
|
||||
|
||||
140
x25519.go
140
x25519.go
@@ -1,40 +1,52 @@
|
||||
// Migration Notes (November 24, 2025):
|
||||
// Migrated to use github.com/go-i2p/crypto/curve25519 package which provides
|
||||
// standardized X25519 key generation and ECDH operations via go.step.sm/crypto/x25519.
|
||||
//
|
||||
// Key Changes:
|
||||
// - X25519KeyPair now wraps curve25519.Curve25519PrivateKey and Curve25519PublicKey (slice types)
|
||||
// - NewX25519KeyPair() delegates to curve25519.GenerateX25519KeyPair()
|
||||
// - GenerateSharedSecret() uses PrivateKey.SharedKey() for ECDH operations
|
||||
// - Maintains [32]byte return types for backward compatibility
|
||||
//
|
||||
// The crypto package uses []byte (slice) types for keys, while I2CP uses
|
||||
// [32]byte arrays. The wrapper maintains I2CP compatibility.
|
||||
|
||||
package go_i2cp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
cryptox25519 "github.com/go-i2p/crypto/curve25519"
|
||||
"go.step.sm/crypto/x25519"
|
||||
)
|
||||
|
||||
// X25519KeyPair represents an X25519 key exchange key pair for modern I2P ECDH
|
||||
// Wraps github.com/go-i2p/crypto/curve25519 types
|
||||
type X25519KeyPair struct {
|
||||
algorithmType uint32
|
||||
privateKey [32]byte
|
||||
publicKey [32]byte
|
||||
privateKey cryptox25519.Curve25519PrivateKey
|
||||
publicKey cryptox25519.Curve25519PublicKey
|
||||
}
|
||||
|
||||
// NewX25519KeyPair generates a new X25519 key pair for ECDH operations
|
||||
// Delegates to github.com/go-i2p/crypto/curve25519.GenerateX25519KeyPair()
|
||||
func NewX25519KeyPair() (*X25519KeyPair, error) {
|
||||
var privateKey, publicKey [32]byte
|
||||
|
||||
// Generate random private key
|
||||
_, err := rand.Read(privateKey[:])
|
||||
// Generate key pair using crypto package
|
||||
pubKey, privKey, err := cryptox25519.GenerateX25519KeyPair()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate X25519 private key: %w", err)
|
||||
return nil, fmt.Errorf("failed to generate X25519 key pair: %w", err)
|
||||
}
|
||||
|
||||
// Derive public key from private key
|
||||
curve25519.ScalarBaseMult(&publicKey, &privateKey)
|
||||
|
||||
// Dereference pointers to get slice values
|
||||
return &X25519KeyPair{
|
||||
algorithmType: X25519,
|
||||
privateKey: privateKey,
|
||||
publicKey: publicKey,
|
||||
privateKey: *privKey,
|
||||
publicKey: *pubKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// X25519KeyPairFromStream reads an X25519 key pair from a stream
|
||||
// Uses go.step.sm/crypto/x25519 for key reconstruction and validation
|
||||
func X25519KeyPairFromStream(stream *Stream) (*X25519KeyPair, error) {
|
||||
var algorithmType uint32
|
||||
var err error
|
||||
@@ -48,22 +60,53 @@ func X25519KeyPairFromStream(stream *Stream) (*X25519KeyPair, error) {
|
||||
return nil, fmt.Errorf("unsupported algorithm type: %d", algorithmType)
|
||||
}
|
||||
|
||||
var kp X25519KeyPair
|
||||
kp.algorithmType = algorithmType
|
||||
|
||||
// Read private key (32 bytes)
|
||||
_, err = stream.Read(kp.privateKey[:])
|
||||
privKeyBytes := make([]byte, 32)
|
||||
_, err = stream.Read(privKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read X25519 private key: %w", err)
|
||||
}
|
||||
|
||||
// Read public key (32 bytes)
|
||||
_, err = stream.Read(kp.publicKey[:])
|
||||
pubKeyBytes := make([]byte, 32)
|
||||
_, err = stream.Read(pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read X25519 public key: %w", err)
|
||||
}
|
||||
|
||||
return &kp, nil
|
||||
// Validate that the public key corresponds to the private key
|
||||
privKey := make(x25519.PrivateKey, 32)
|
||||
copy(privKey, privKeyBytes)
|
||||
|
||||
expectedPubKeyInterface := privKey.Public()
|
||||
expectedPubKey, ok := expectedPubKeyInterface.(x25519.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert public key to x25519.PublicKey")
|
||||
}
|
||||
|
||||
// Compare public keys
|
||||
if !bytesEqual(expectedPubKey, pubKeyBytes) {
|
||||
return nil, fmt.Errorf("public key does not match private key")
|
||||
}
|
||||
|
||||
return &X25519KeyPair{
|
||||
algorithmType: algorithmType,
|
||||
privateKey: cryptox25519.Curve25519PrivateKey(privKeyBytes),
|
||||
publicKey: cryptox25519.Curve25519PublicKey(pubKeyBytes),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// bytesEqual compares two byte slices for equality
|
||||
func bytesEqual(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// X25519PublicKeyFromStream reads only the public key from a stream
|
||||
@@ -77,19 +120,36 @@ func X25519PublicKeyFromStream(stream *Stream) ([32]byte, error) {
|
||||
}
|
||||
|
||||
// GenerateSharedSecret performs ECDH to generate a shared secret with the peer's public key
|
||||
// Uses go.step.sm/crypto/x25519 PrivateKey.SharedKey() for ECDH
|
||||
func (kp *X25519KeyPair) GenerateSharedSecret(peerPublicKey [32]byte) ([32]byte, error) {
|
||||
var sharedSecret [32]byte
|
||||
var result [32]byte
|
||||
|
||||
// Perform scalar multiplication: shared_secret = our_private * peer_public
|
||||
curve25519.ScalarMult(&sharedSecret, &kp.privateKey, &peerPublicKey)
|
||||
|
||||
// Check for weak shared secrets (all zeros)
|
||||
var zero [32]byte
|
||||
if sharedSecret == zero {
|
||||
return sharedSecret, fmt.Errorf("generated weak shared secret (all zeros)")
|
||||
// Validate the peer's public key first
|
||||
if !ValidateX25519PublicKey(peerPublicKey) {
|
||||
return result, fmt.Errorf("invalid peer public key")
|
||||
}
|
||||
|
||||
return sharedSecret, nil
|
||||
// Convert slice to x25519 types for SharedKey operation
|
||||
privKey := make(x25519.PrivateKey, 32)
|
||||
copy(privKey, kp.privateKey)
|
||||
|
||||
pubKey := make(x25519.PublicKey, 32)
|
||||
copy(pubKey, peerPublicKey[:])
|
||||
|
||||
// Perform X25519 key exchange (ECDH)
|
||||
sharedSecret, err := privKey.SharedKey(pubKey)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("X25519 key exchange failed: %w", err)
|
||||
}
|
||||
|
||||
// Additional security check: reject all-zero shared secrets
|
||||
var zero [32]byte
|
||||
copy(result[:], sharedSecret)
|
||||
if result == zero {
|
||||
return result, fmt.Errorf("generated weak shared secret (all zeros)")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// WriteToStream writes the complete X25519 key pair to a stream
|
||||
@@ -104,14 +164,14 @@ func (kp *X25519KeyPair) WriteToStream(stream *Stream) error {
|
||||
return fmt.Errorf("failed to write algorithm type: %w", err)
|
||||
}
|
||||
|
||||
// Write private key
|
||||
_, err = stream.Write(kp.privateKey[:])
|
||||
// Write private key (convert slice to bytes)
|
||||
_, err = stream.Write(kp.privateKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write X25519 private key: %w", err)
|
||||
}
|
||||
|
||||
// Write public key
|
||||
_, err = stream.Write(kp.publicKey[:])
|
||||
// Write public key (convert slice to bytes)
|
||||
_, err = stream.Write(kp.publicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write X25519 public key: %w", err)
|
||||
}
|
||||
@@ -125,7 +185,7 @@ func (kp *X25519KeyPair) WritePublicKeyToStream(stream *Stream) error {
|
||||
return fmt.Errorf("stream cannot be nil")
|
||||
}
|
||||
|
||||
_, err := stream.Write(kp.publicKey[:])
|
||||
_, err := stream.Write(kp.publicKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write X25519 public key: %w", err)
|
||||
}
|
||||
@@ -133,14 +193,18 @@ func (kp *X25519KeyPair) WritePublicKeyToStream(stream *Stream) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PublicKey returns a copy of the public key
|
||||
// PublicKey returns a copy of the public key as [32]byte for backward compatibility
|
||||
func (kp *X25519KeyPair) PublicKey() [32]byte {
|
||||
return kp.publicKey
|
||||
var result [32]byte
|
||||
copy(result[:], kp.publicKey)
|
||||
return result
|
||||
}
|
||||
|
||||
// PrivateKey returns a copy of the private key
|
||||
// PrivateKey returns a copy of the private key as [32]byte for backward compatibility
|
||||
func (kp *X25519KeyPair) PrivateKey() [32]byte {
|
||||
return kp.privateKey
|
||||
var result [32]byte
|
||||
copy(result[:], kp.privateKey)
|
||||
return result
|
||||
}
|
||||
|
||||
// AlgorithmType returns the algorithm type constant
|
||||
|
||||
Reference in New Issue
Block a user