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.
// 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{
"input_length": len(data),
}).Debug("Constructing publicKey from keyCertificate")

View File

@@ -80,7 +80,7 @@ total length: 387+ bytes
// https://geti2p.net/spec/common-structures#keysandcert
type KeysAndCert struct {
KeyCertificate *KeyCertificate
publicKey crypto.PublicKey
publicKey crypto.RecievingPublicKey
Padding []byte
signingPublicKey crypto.SigningPublicKey
}
@@ -104,7 +104,7 @@ func (keys_and_cert KeysAndCert) Bytes() []byte {
}
// 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
}
@@ -248,7 +248,7 @@ func ReadKeysAndCertElgAndEd25519(data []byte) (keysAndCert *KeysAndCert, remain
return
}
func constructPublicKey(data []byte, cryptoType uint16) (crypto.PublicKey, error) {
func constructPublicKey(data []byte, cryptoType uint16) (crypto.RecievingPublicKey, error) {
switch cryptoType {
case CRYPTO_KEY_TYPE_ELGAMAL:
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.
func NewKeysAndCert(
keyCertificate *KeyCertificate,
publicKey crypto.PublicKey,
publicKey crypto.RecievingPublicKey,
padding []byte,
signingPublicKey crypto.SigningPublicKey,
) (*KeysAndCert, error) {

View File

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

View File

@@ -4,13 +4,14 @@ import (
"bytes"
"crypto/rand"
"fmt"
"testing"
"time"
"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/router_address"
"github.com/go-i2p/go-i2p/lib/common/router_info"
"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/keys_and_cert"
@@ -23,7 +24,7 @@ import (
"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)
var ed25519_privkey crypto.Ed25519PrivateKey
_, 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)
// Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey
var _ crypto.RecievingPublicKey = elg_pubkey
// Create KeyCertificate specifying key types
var payload bytes.Buffer
@@ -176,7 +177,7 @@ func createTestLease(t *testing.T, index int, routerInfo *router_info.RouterInfo
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)
var ed25519_privkey crypto.Ed25519PrivateKey
_, err := (&ed25519_privkey).Generate()

View File

@@ -52,7 +52,7 @@ func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder
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")
// 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)
// Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey
var _ crypto.RecievingPublicKey = elg_pubkey
// Create KeyCertificate specifying key types
var payload bytes.Buffer

View File

@@ -3,10 +3,11 @@ package router_info
import (
"bytes"
"crypto/rand"
"github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
"testing"
"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/data"
"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)
// Ensure that elg_pubkey implements crypto.PublicKey interface
var _ crypto.PublicKey = elg_pubkey
var _ crypto.RecievingPublicKey = elg_pubkey
// Create KeyCertificate specifying key types
var payload bytes.Buffer

View File

@@ -1,6 +1,7 @@
package crypto
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
@@ -185,9 +186,79 @@ func (v *Ed25519Verifier) Verify(data, sig []byte) (err error) {
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) {
// TODO implement me
panic("implement me")
if len(k) != ed25519.PrivateKeySize {
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) {

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
Bytes() []byte
}
type PublicKey interface {
type RecievingPublicKey interface {
Len() int
Bytes() []byte
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
import (
"strconv"
"time"
"github.com/go-i2p/logger"
@@ -89,7 +90,7 @@ func (r *Router) mainloop() {
log.WithError(err).Error("Failed to ensure NetDB")
}
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 {
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