start working on basic key storage for own RI private keys

This commit is contained in:
eyedeekay
2025-02-15 22:16:31 -05:00
parent 71fff44d4b
commit 036f9116a5
16 changed files with 317 additions and 19 deletions

View File

@@ -132,7 +132,7 @@ func (keyCertificate KeyCertificate) PublicKeyType() (pubkey_type int) {
// ConstructPublicKey returns a publicKey constructed using any excess data that may be stored in the KeyCertififcate. // ConstructPublicKey returns a publicKey constructed using any excess data that may be stored in the KeyCertififcate.
// Returns enr errors encountered while parsing. // Returns enr errors encountered while parsing.
func (keyCertificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error) { func (keyCertificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.RecievingPublicKey, err error) {
log.WithFields(logrus.Fields{ log.WithFields(logrus.Fields{
"input_length": len(data), "input_length": len(data),
}).Debug("Constructing publicKey from keyCertificate") }).Debug("Constructing publicKey from keyCertificate")

View File

@@ -80,7 +80,7 @@ total length: 387+ bytes
// https://geti2p.net/spec/common-structures#keysandcert // https://geti2p.net/spec/common-structures#keysandcert
type KeysAndCert struct { type KeysAndCert struct {
KeyCertificate *KeyCertificate KeyCertificate *KeyCertificate
publicKey crypto.PublicKey publicKey crypto.RecievingPublicKey
Padding []byte Padding []byte
signingPublicKey crypto.SigningPublicKey signingPublicKey crypto.SigningPublicKey
} }
@@ -104,7 +104,7 @@ func (keys_and_cert KeysAndCert) Bytes() []byte {
} }
// publicKey returns the public key as a crypto.publicKey. // publicKey returns the public key as a crypto.publicKey.
func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.PublicKey) { func (keys_and_cert *KeysAndCert) PublicKey() (key crypto.RecievingPublicKey) {
return keys_and_cert.publicKey return keys_and_cert.publicKey
} }
@@ -248,7 +248,7 @@ func ReadKeysAndCertElgAndEd25519(data []byte) (keysAndCert *KeysAndCert, remain
return return
} }
func constructPublicKey(data []byte, cryptoType uint16) (crypto.PublicKey, error) { func constructPublicKey(data []byte, cryptoType uint16) (crypto.RecievingPublicKey, error) {
switch cryptoType { switch cryptoType {
case CRYPTO_KEY_TYPE_ELGAMAL: case CRYPTO_KEY_TYPE_ELGAMAL:
if len(data) != 256 { if len(data) != 256 {
@@ -280,7 +280,7 @@ func constructSigningPublicKey(data []byte, sigType uint16) (crypto.SigningPubli
// It validates the sizes of the provided keys and padding before assembling the struct. // It validates the sizes of the provided keys and padding before assembling the struct.
func NewKeysAndCert( func NewKeysAndCert(
keyCertificate *KeyCertificate, keyCertificate *KeyCertificate,
publicKey crypto.PublicKey, publicKey crypto.RecievingPublicKey,
padding []byte, padding []byte,
signingPublicKey crypto.SigningPublicKey, signingPublicKey crypto.SigningPublicKey,
) (*KeysAndCert, error) { ) (*KeysAndCert, error) {

View File

@@ -492,7 +492,7 @@ func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
func NewLeaseSet( func NewLeaseSet(
destination Destination, destination Destination,
encryptionKey crypto.PublicKey, encryptionKey crypto.RecievingPublicKey,
signingKey crypto.SigningPublicKey, signingKey crypto.SigningPublicKey,
leases []Lease, leases []Lease,
signingPrivateKey crypto.SigningPrivateKey, signingPrivateKey crypto.SigningPrivateKey,

View File

@@ -4,13 +4,14 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"testing"
"time"
"github.com/go-i2p/go-i2p/lib/common/destination" "github.com/go-i2p/go-i2p/lib/common/destination"
"github.com/go-i2p/go-i2p/lib/common/key_certificate" "github.com/go-i2p/go-i2p/lib/common/key_certificate"
"github.com/go-i2p/go-i2p/lib/common/router_address" "github.com/go-i2p/go-i2p/lib/common/router_address"
"github.com/go-i2p/go-i2p/lib/common/router_info" "github.com/go-i2p/go-i2p/lib/common/router_info"
"github.com/go-i2p/go-i2p/lib/common/signature" "github.com/go-i2p/go-i2p/lib/common/signature"
"testing"
"time"
"github.com/go-i2p/go-i2p/lib/common/data" "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/common/keys_and_cert" "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
@@ -23,7 +24,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func generateTestRouterInfo(t *testing.T) (*router_info.RouterInfo, crypto.PublicKey, crypto.SigningPublicKey, crypto.SigningPrivateKey, crypto.SigningPrivateKey, error) { func generateTestRouterInfo(t *testing.T) (*router_info.RouterInfo, crypto.RecievingPublicKey, crypto.SigningPublicKey, crypto.SigningPrivateKey, crypto.SigningPrivateKey, error) {
// Generate signing key pair (Ed25519) // Generate signing key pair (Ed25519)
var ed25519_privkey crypto.Ed25519PrivateKey var ed25519_privkey crypto.Ed25519PrivateKey
_, err := (&ed25519_privkey).Generate() _, err := (&ed25519_privkey).Generate()
@@ -63,7 +64,7 @@ func generateTestRouterInfo(t *testing.T) (*router_info.RouterInfo, crypto.Publi
copy(elg_pubkey[256-len(yBytes):], yBytes) copy(elg_pubkey[256-len(yBytes):], yBytes)
// Ensure that elg_pubkey implements crypto.PublicKey interface // Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey var _ crypto.RecievingPublicKey = elg_pubkey
// Create KeyCertificate specifying key types // Create KeyCertificate specifying key types
var payload bytes.Buffer var payload bytes.Buffer
@@ -176,7 +177,7 @@ func createTestLease(t *testing.T, index int, routerInfo *router_info.RouterInfo
return testLease, nil return testLease, nil
} }
func generateTestDestination(t *testing.T) (*destination.Destination, crypto.PublicKey, crypto.SigningPublicKey, crypto.SigningPrivateKey, error) { func generateTestDestination(t *testing.T) (*destination.Destination, crypto.RecievingPublicKey, crypto.SigningPublicKey, crypto.SigningPrivateKey, error) {
// Generate client signing key pair (Ed25519) // Generate client signing key pair (Ed25519)
var ed25519_privkey crypto.Ed25519PrivateKey var ed25519_privkey crypto.Ed25519PrivateKey
_, err := (&ed25519_privkey).Generate() _, err := (&ed25519_privkey).Generate()

View File

@@ -52,7 +52,7 @@ func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder
return return
} }
func NewRouterIdentity(publicKey crypto.PublicKey, signingPublicKey crypto.SigningPublicKey, cert certificate.Certificate, padding []byte) (*RouterIdentity, error) { func NewRouterIdentity(publicKey crypto.RecievingPublicKey, signingPublicKey crypto.SigningPublicKey, cert certificate.Certificate, padding []byte) (*RouterIdentity, error) {
log.Debug("Creating new RouterIdentity") log.Debug("Creating new RouterIdentity")
// Step 1: Create keyCertificate from the provided certificate. // Step 1: Create keyCertificate from the provided certificate.

View File

@@ -0,0 +1,9 @@
package router_info
import "github.com/go-i2p/go-i2p/lib/common/key_certificate"
func OwnedRouterInfo(keyCertificate key_certificate.KeyCertificate) *RouterInfo {
return &RouterInfo{
// ...
}
}

View File

@@ -59,7 +59,7 @@ func TestCreateRouterInfo(t *testing.T) {
copy(elg_pubkey[256-len(yBytes):], yBytes) copy(elg_pubkey[256-len(yBytes):], yBytes)
// Ensure that elg_pubkey implements crypto.PublicKey interface // Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey var _ crypto.RecievingPublicKey = elg_pubkey
// Create KeyCertificate specifying key types // Create KeyCertificate specifying key types
var payload bytes.Buffer var payload bytes.Buffer

View File

@@ -3,10 +3,11 @@ package router_info
import ( import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
"testing" "testing"
"time" "time"
"github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
"github.com/go-i2p/go-i2p/lib/common/certificate" "github.com/go-i2p/go-i2p/lib/common/certificate"
"github.com/go-i2p/go-i2p/lib/common/data" "github.com/go-i2p/go-i2p/lib/common/data"
"github.com/go-i2p/go-i2p/lib/common/key_certificate" "github.com/go-i2p/go-i2p/lib/common/key_certificate"
@@ -59,7 +60,7 @@ func generateTestRouterInfo(t *testing.T, publishedTime time.Time) (*RouterInfo,
copy(elg_pubkey[256-len(yBytes):], yBytes) copy(elg_pubkey[256-len(yBytes):], yBytes)
// Ensure that elg_pubkey implements crypto.PublicKey interface // Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey var _ crypto.RecievingPublicKey = elg_pubkey
// Create KeyCertificate specifying key types // Create KeyCertificate specifying key types
var payload bytes.Buffer var payload bytes.Buffer

View File

@@ -1,6 +1,7 @@
package crypto package crypto
import ( import (
"bytes"
"crypto/ed25519" "crypto/ed25519"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
@@ -185,9 +186,79 @@ func (v *Ed25519Verifier) Verify(data, sig []byte) (err error) {
type Ed25519PrivateKey ed25519.PrivateKey type Ed25519PrivateKey ed25519.PrivateKey
func (k Ed25519PrivateKey) Bytes() []byte {
return k
}
func (k Ed25519PrivateKey) Zero() {
for i := range k {
k[i] = 0
}
}
func (k Ed25519PrivateKey) NewDecrypter() (Decrypter, error) { func (k Ed25519PrivateKey) NewDecrypter() (Decrypter, error) {
// TODO implement me if len(k) != ed25519.PrivateKeySize {
panic("implement me") return nil, errors.New("invalid ed25519 private key size")
}
d := &Ed25519Decrypter{
privateKey: k,
}
return d, nil
}
type Ed25519Decrypter struct {
privateKey Ed25519PrivateKey
}
func (d *Ed25519Decrypter) Decrypt(data []byte) ([]byte, error) {
return d.DecryptPadding(data, true)
}
func (d *Ed25519Decrypter) DecryptPadding(data []byte, zeroPadding bool) ([]byte, error) {
if len(data) != 514 && len(data) != 512 {
return nil, errors.New("invalid ciphertext length")
}
// Extract components based on padding
var aBytes, bBytes []byte
if zeroPadding {
aBytes = data[1:258]
bBytes = data[258:]
} else {
aBytes = data[0:256]
bBytes = data[256:]
}
// Convert to big integers
a := new(big.Int).SetBytes(aBytes)
b := new(big.Int).SetBytes(bBytes)
// Compute p = 2^255 - 19
p := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 255), big.NewInt(19))
// Use private key to decrypt
m := new(big.Int).ModInverse(a, p)
if m == nil {
return nil, errors.New("decryption failed: modular inverse does not exist")
}
decrypted := new(big.Int).Mod(new(big.Int).Mul(b, m), p).Bytes()
// Remove padding and validate hash
if len(decrypted) < 33 {
return nil, errors.New("decryption failed: result too short")
}
hashBytes := decrypted[1:33]
message := decrypted[33:]
// Verify hash
actualHash := sha256.Sum256(message)
if !bytes.Equal(hashBytes, actualHash[:]) {
return nil, errors.New("decryption failed: hash verification failed")
}
return message, nil
} }
func (k Ed25519PrivateKey) NewSigner() (Signer, error) { func (k Ed25519PrivateKey) NewSigner() (Signer, error) {

11
lib/crypto/privatekey.go Normal file
View File

@@ -0,0 +1,11 @@
package crypto
// PrivateKey is an interface for private keys
type PrivateKey interface {
// Public returns the public key corresponding to this private key
Public() (SigningPublicKey, error)
// Bytes returns the raw bytes of this private key
Bytes() []byte
// Zero clears all sensitive data from the private key
Zero()
}

6
lib/crypto/public.go Normal file
View File

@@ -0,0 +1,6 @@
package crypto
type PublicKey interface {
Len() int
Bytes() []byte
}

View File

@@ -28,7 +28,7 @@ type SigningPublicKey interface {
Len() int Len() int
Bytes() []byte Bytes() []byte
} }
type PublicKey interface { type RecievingPublicKey interface {
Len() int Len() int
Bytes() []byte Bytes() []byte
NewEncrypter() (Encrypter, error) NewEncrypter() (Encrypter, error)

View File

@@ -0,0 +1,109 @@
package keys
import (
"crypto/ed25519"
"crypto/rand"
"errors"
"os"
"path/filepath"
"github.com/go-i2p/go-i2p/lib/crypto"
)
// RouterInfoKeystore is an implementation of KeyStore for storing and retrieving RouterInfo private keys and exporting RouterInfos
type RouterInfoKeystore struct {
dir string
name string
privateKey crypto.PrivateKey
}
var riks KeyStore = &RouterInfoKeystore{}
// NewRouterInfoKeystore creates a new RouterInfoKeystore with fresh and new private keys
// it accepts a directory to store the keys in and a name for the keys
// then it generates new private keys for the routerInfo if none exist
func NewRouterInfoKeystore(dir, name string) (*RouterInfoKeystore, error) {
if _, err := os.Stat(dir); os.IsNotExist(err) {
err := os.MkdirAll(dir, 0755)
if err != nil {
return nil, err
}
}
var privateKey crypto.PrivateKey
fullPath := filepath.Join(dir, name)
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
privateKey, err = generateNewKey()
if err != nil {
return nil, err
}
} else {
keyData, err := os.ReadFile(fullPath)
if err != nil {
return nil, err
}
privateKey, err = loadExistingKey(keyData)
if err != nil {
return nil, err
}
}
return &RouterInfoKeystore{
dir: dir,
name: name,
privateKey: privateKey,
}, nil
}
func generateNewKey() (crypto.Ed25519PrivateKey, error) {
// Generate a new key pair
_, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
// Convert to our type
return crypto.Ed25519PrivateKey(priv), nil
}
func loadExistingKey(keyData []byte) (crypto.Ed25519PrivateKey, error) {
// Validate key length
if len(keyData) != ed25519.PrivateKeySize {
return nil, errors.New("invalid key length")
}
// Convert to our type
return crypto.Ed25519PrivateKey(keyData), nil
}
func (ks *RouterInfoKeystore) GetKeys() (crypto.PublicKey, crypto.PrivateKey, error) {
public, err := ks.privateKey.Public()
if err != nil {
return nil, nil, err
}
return public, ks.privateKey, nil
}
func (ks *RouterInfoKeystore) StoreKeys() error {
if _, err := os.Stat(ks.dir); os.IsNotExist(err) {
err := os.MkdirAll(ks.dir, 0755)
if err != nil {
return err
}
}
// on the disk somewhere
filename := filepath.Join(ks.dir, ks.name)
return os.WriteFile(filename, ks.privateKey.Bytes(), 0644)
}
func (ks *RouterInfoKeystore) KeyID() string {
if ks.name == "" {
public, err := ks.privateKey.Public()
if err != nil {
return "error"
}
if len(public.Bytes()) > 10 {
return string(public.Bytes()[:10])
}
return string(public.Bytes())
}
return ks.name
}

67
lib/keys/types.go Normal file
View File

@@ -0,0 +1,67 @@
package keys
import (
"fmt"
"os"
"github.com/go-i2p/go-i2p/lib/crypto"
)
// KeyStore is an interface for storing and retrieving keys
type KeyStore interface {
KeyID() string
// GetKeys returns the public and private keys
GetKeys() (publicKey crypto.PublicKey, privateKey crypto.PrivateKey, err error)
// StoreKeys stores the keys
StoreKeys() error
}
type KeyStoreImpl struct {
dir string
name string
privateKey crypto.PrivateKey
}
func NewKeyStoreImpl(dir, name string, privateKey crypto.PrivateKey) *KeyStoreImpl {
return &KeyStoreImpl{
dir: dir,
name: name,
privateKey: privateKey,
}
}
func (ks *KeyStoreImpl) KeyID() string {
if ks.name == "" {
public, err := ks.privateKey.Public()
if err != nil {
return "error"
}
if len(public.Bytes()) > 10 {
return string(public.Bytes()[:10])
} else {
return string(public.Bytes())
}
}
return ks.name
}
func (ks *KeyStoreImpl) GetKeys() (crypto.PublicKey, crypto.PrivateKey, error) {
public, err := ks.privateKey.Public()
if err != nil {
return nil, nil, err
}
return public, ks.privateKey, nil
}
func (ks *KeyStoreImpl) StoreKeys() error {
// make sure the directory exists
if _, err := os.Stat(ks.dir); os.IsNotExist(err) {
err := os.MkdirAll(ks.dir, 0755)
if err != nil {
return err
}
}
// on the disk somewhere
filename := fmt.Sprintf("private-%s.key", ks.KeyID())
return os.WriteFile(filename, ks.privateKey.Bytes(), 0644)
}

View File

@@ -1,6 +1,7 @@
package router package router
import ( import (
"strconv"
"time" "time"
"github.com/go-i2p/logger" "github.com/go-i2p/logger"
@@ -89,7 +90,7 @@ func (r *Router) mainloop() {
log.WithError(err).Error("Failed to ensure NetDB") log.WithError(err).Error("Failed to ensure NetDB")
} }
if sz := r.ndb.Size(); sz >= 0 { if sz := r.ndb.Size(); sz >= 0 {
log.WithField("size", sz).Debug("NetDB Size") log.WithField("size", sz).Debug("NetDB Size: " + strconv.Itoa(sz))
} else { } else {
log.Warn("Unable to determine NetDB size") log.Warn("Unable to determine NetDB size")
} }

22
lib/transport/obfs/doc.md Normal file
View File

@@ -0,0 +1,22 @@
# obfs
--
import "github.com/go-i2p/go-i2p/lib/transport/obfs"
## Usage
#### func DeobfuscateEphemeralKey
```go
func DeobfuscateEphemeralKey(message []byte, aesKey *crypto.AESSymmetricKey) ([]byte, error)
```
DeobfuscateEphemeralKey decrypts the ephemeral public key in the message using
AES-256-CBC without padding
#### func ObfuscateEphemeralKey
```go
func ObfuscateEphemeralKey(message []byte, aesKey *crypto.AESSymmetricKey) ([]byte, error)
```
ObfuscateEphemeralKey encrypts the ephemeral public key in the message using
AES-256-CBC without padding