mirror of
https://github.com/go-i2p/go-i2p.git
synced 2025-06-07 10:01:41 -04:00
583 lines
19 KiB
Go
583 lines
19 KiB
Go
// Package lease_set implements the I2P LeastSet common data structure
|
|
package lease_set
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/go-i2p/go-i2p/lib/common/signature"
|
|
"github.com/samber/oops"
|
|
|
|
"github.com/go-i2p/logger"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
. "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/destination"
|
|
. "github.com/go-i2p/go-i2p/lib/common/key_certificate"
|
|
. "github.com/go-i2p/go-i2p/lib/common/keys_and_cert"
|
|
. "github.com/go-i2p/go-i2p/lib/common/lease"
|
|
"github.com/go-i2p/go-i2p/lib/crypto"
|
|
)
|
|
|
|
var log = logger.GetGoI2PLogger()
|
|
|
|
// Sizes of various structures in an I2P LeaseSet
|
|
const (
|
|
LEASE_SET_PUBKEY_SIZE = 256
|
|
LEASE_SET_SPK_SIZE = 128
|
|
LEASE_SET_SIG_SIZE = 40
|
|
)
|
|
|
|
/*
|
|
[LeaseSet]
|
|
Accurate for version 0.9.49
|
|
|
|
Description
|
|
Contains all of the currently authorized Leases for a particular Destination, the
|
|
publicKey to which garlic messages can be encrypted, and then the signingPublicKey
|
|
that can be used to revoke this particular version of the structure. The LeaseSet is one
|
|
of the two structures stored in the network database (the other being RouterInfo), and
|
|
is kered under the SHA256 of the contained Destination.
|
|
|
|
Contents
|
|
Destination, followed by a publicKey for encryption, then a signingPublicKey which
|
|
can be used to revoke this version of the LeaseSet, then a 1 byte Integer specifying how
|
|
many Lease structures are in the set, followed by the actual Lease structures and
|
|
finally a Signature of the previous bytes signed by the Destination's SigningPrivateKey.
|
|
|
|
+----+----+----+----+----+----+----+----+
|
|
| destination |
|
|
+ +
|
|
| |
|
|
~ ~
|
|
~ ~
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| encryption_key |
|
|
+ +
|
|
| |
|
|
~ ~
|
|
~ ~
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| signing_key |
|
|
+ +
|
|
| |
|
|
~ ~
|
|
~ ~
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|num | Lease 0 |
|
|
+----+ +
|
|
| |
|
|
~ ~
|
|
~ ~
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| Lease 1 |
|
|
+ +
|
|
| |
|
|
~ ~
|
|
~ ~
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| Lease ($num-1) |
|
|
+ +
|
|
| |
|
|
~ ~
|
|
~ ~
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
| signature |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+ +
|
|
| |
|
|
+----+----+----+----+----+----+----+----+
|
|
|
|
destination :: Destination
|
|
length -> >= 387 bytes
|
|
|
|
encryption_key :: publicKey
|
|
length -> 256 bytes
|
|
|
|
signing_key :: signingPublicKey
|
|
length -> 128 bytes or as specified in destination's key certificate
|
|
|
|
num :: Integer
|
|
length -> 1 byte
|
|
Number of leases to follow
|
|
value: 0 <= num <= 16
|
|
|
|
leases :: [Lease]
|
|
length -> $num*44 bytes
|
|
|
|
signature :: Signature
|
|
length -> 40 bytes or as specified in destination's key certificate
|
|
*/
|
|
|
|
// LeaseSet is the represenation of an I2P LeaseSet.
|
|
//
|
|
// https://geti2p.net/spec/common-structures#leaseset
|
|
type LeaseSet []byte
|
|
|
|
/*
|
|
type LeaseSet struct {
|
|
Destination *Destination
|
|
EncryptionKey *crypto.ElgPublicKey
|
|
SigningKey *crypto.ElgPublicKey
|
|
Size *Integer
|
|
Leases []*Lease
|
|
Signature *Signature
|
|
}
|
|
*/
|
|
|
|
// Destination returns the Destination as []byte.
|
|
func (lease_set LeaseSet) Destination() (destination Destination, err error) {
|
|
keys_and_cert, _, err := ReadKeysAndCertElgAndEd25519(lease_set)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to read KeysAndCert from LeaseSet")
|
|
return
|
|
}
|
|
destination, _, err = ReadDestination(keys_and_cert.Bytes())
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to read Destination from KeysAndCert")
|
|
} else {
|
|
log.Debug("Successfully retrieved Destination from LeaseSet")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (lease_set LeaseSet) DestinationDeux() (destination Destination, err error) {
|
|
data := lease_set
|
|
|
|
fmt.Printf("Starting DestinationDeux, lease_set_length=%d\n", len(data))
|
|
|
|
// Read the Destination (KeysAndCert) from the LeaseSet
|
|
destination, remainder, err := ReadDestinationFromLeaseSet(data)
|
|
if err != nil {
|
|
fmt.Printf("Failed to read Destination from LeaseSet: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Successfully retrieved Destination from LeaseSet\n")
|
|
fmt.Printf(" destination_length: %d\n", len(data)-len(remainder))
|
|
fmt.Printf(" remainder_length: %d\n", len(remainder))
|
|
|
|
return
|
|
}
|
|
|
|
func ReadDestinationFromLeaseSet(data []byte) (destination Destination, remainder []byte, err error) {
|
|
fmt.Printf("Reading Destination from LeaseSet, input_length=%d\n", len(data))
|
|
|
|
if len(data) < 387 { // Minimum size of Destination (384 keys + 3 bytes for minimum certificate)
|
|
err = oops.Errorf("LeaseSet data too short to contain Destination")
|
|
fmt.Printf("Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
certDataStart := 384
|
|
certData := data[certDataStart:]
|
|
|
|
cert, _, err := ReadCertificate(certData)
|
|
if err != nil {
|
|
fmt.Printf("Failed to read Certificate from LeaseSet: %v\n", err)
|
|
return
|
|
}
|
|
|
|
certTotalLength := 3 + int(cert.Length())
|
|
destinationLength := certDataStart + certTotalLength
|
|
|
|
fmt.Printf("Certificate details:\n")
|
|
fmt.Printf(" certType: %d\n", cert.Type())
|
|
fmt.Printf(" certLength: %d\n", cert.Length())
|
|
fmt.Printf(" certTotalLength: %d\n", certTotalLength)
|
|
fmt.Printf(" destinationLength: %d\n", destinationLength)
|
|
|
|
if len(data) < destinationLength {
|
|
err = oops.Errorf("LeaseSet data too short to contain full Destination")
|
|
fmt.Printf("Error: %v\n", err)
|
|
return
|
|
}
|
|
|
|
destinationData := data[:destinationLength]
|
|
|
|
keysAndCert, _, err := ReadKeysAndCert(destinationData)
|
|
if err != nil {
|
|
fmt.Printf("Failed to read KeysAndCert: %v\n", err) // 32 / 0 error
|
|
return
|
|
}
|
|
|
|
destination = Destination{
|
|
KeysAndCert: keysAndCert,
|
|
}
|
|
|
|
remainder = data[destinationLength:]
|
|
|
|
fmt.Printf("Successfully read Destination from LeaseSet\n")
|
|
fmt.Printf(" destination_length: %d\n", destinationLength)
|
|
fmt.Printf(" remainder_length: %d\n", len(remainder))
|
|
|
|
return
|
|
}
|
|
|
|
// PublicKey returns the public key as crypto.ElgPublicKey.
|
|
// Returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error) {
|
|
_, remainder, err := ReadKeysAndCert(lease_set)
|
|
remainder_len := len(remainder)
|
|
if remainder_len < LEASE_SET_PUBKEY_SIZE {
|
|
log.WithFields(logrus.Fields{
|
|
"at": "(LeaseSet) publicKey",
|
|
"data_len": remainder_len,
|
|
"required_len": LEASE_SET_PUBKEY_SIZE,
|
|
"reason": "not enough data",
|
|
}).Error("error parsing public key")
|
|
err = oops.Errorf("error parsing public key: not enough data")
|
|
copy(public_key[:], remainder)
|
|
return
|
|
}
|
|
copy(public_key[:], remainder[:LEASE_SET_PUBKEY_SIZE])
|
|
log.Debug("Successfully retrieved publicKey from LeaseSet")
|
|
return
|
|
}
|
|
|
|
// SigningKey returns the signing public key as crypto.SigningPublicKey.
|
|
// returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
|
log.Debug("Retrieving SigningKey from LeaseSet")
|
|
destination, err := lease_set.Destination()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve Destination for SigningKey")
|
|
return
|
|
}
|
|
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE
|
|
cert := destination.Certificate()
|
|
cert_len := cert.Length()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to get Certificate length")
|
|
return
|
|
}
|
|
lease_set_len := len(lease_set)
|
|
if lease_set_len < offset+LEASE_SET_SPK_SIZE {
|
|
log.WithFields(logrus.Fields{
|
|
"at": "(LeaseSet) SigningKey",
|
|
"data_len": lease_set_len,
|
|
"required_len": offset + LEASE_SET_SPK_SIZE,
|
|
"reason": "not enough data",
|
|
}).Error("error parsing signing public key")
|
|
err = oops.Errorf("error parsing signing public key: not enough data")
|
|
return
|
|
}
|
|
if cert_len == 0 {
|
|
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
|
|
// signingPublicKey space as legacy DSA SHA1 signingPublicKey.
|
|
var dsa_pk crypto.DSAPublicKey
|
|
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
|
|
signing_public_key = dsa_pk
|
|
log.Debug("Retrieved legacy DSA SHA1 signingPublicKey")
|
|
} else {
|
|
// A Certificate is present in this LeaseSet's Destination
|
|
cert_type := cert.Type()
|
|
if cert_type == CERT_KEY {
|
|
// This LeaseSet's Destination's Certificate is a Key Certificate,
|
|
// create the signing publickey key using any data that might be
|
|
// contained in the key certificate.
|
|
keyCert, err := KeyCertificateFromCertificate(cert)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to create keyCert")
|
|
}
|
|
signing_public_key, err = keyCert.ConstructSigningPublicKey(
|
|
lease_set[offset : offset+LEASE_SET_SPK_SIZE],
|
|
)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to construct signingPublicKey from keyCertificate")
|
|
} else {
|
|
log.Debug("Retrieved signingPublicKey from keyCertificate")
|
|
}
|
|
} else {
|
|
// No Certificate is present, return the LEASE_SET_SPK_SIZE byte
|
|
// signingPublicKey space as legacy DSA SHA1 signingPublicKey.
|
|
var dsa_pk crypto.DSAPublicKey
|
|
copy(dsa_pk[:], lease_set[offset:offset+LEASE_SET_SPK_SIZE])
|
|
signing_public_key = dsa_pk
|
|
log.Debug("Retrieved legacy DSA SHA1 signingPublicKey (Certificate present but not Key Certificate)")
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// LeaseCount returns the numbert of leases specified by the LeaseCount value as int.
|
|
// returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
|
log.Debug("Retrieving LeaseCount from LeaseSet")
|
|
_, remainder, err := ReadKeysAndCert(lease_set)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to read KeysAndCert for LeaseCount")
|
|
return
|
|
}
|
|
remainder_len := len(remainder)
|
|
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
|
|
log.WithFields(logrus.Fields{
|
|
"at": "(LeaseSet) LeaseCount",
|
|
"data_len": remainder_len,
|
|
"required_len": LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1,
|
|
"reason": "not enough data",
|
|
}).Error("error parsing lease count")
|
|
err = oops.Errorf("error parsing lease count: not enough data")
|
|
return
|
|
}
|
|
c := Integer([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
|
|
count = c.Int()
|
|
if count > 16 {
|
|
log.WithFields(logrus.Fields{
|
|
"at": "(LeaseSet) LeaseCount",
|
|
"lease_count": count,
|
|
"reason": "more than 16 leases",
|
|
}).Warn("invalid lease set")
|
|
err = oops.Errorf("invalid lease set: more than 16 leases")
|
|
} else {
|
|
log.WithField("lease_count", count).Debug("Retrieved LeaseCount from LeaseSet")
|
|
}
|
|
return
|
|
}
|
|
|
|
// Leases returns the leases as []Lease.
|
|
// returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
|
log.Debug("Retrieving Leases from LeaseSet")
|
|
destination, err := lease_set.Destination()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve Destination for Leases")
|
|
return
|
|
}
|
|
offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
|
|
count, err := lease_set.LeaseCount()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve LeaseCount for Leases")
|
|
return
|
|
}
|
|
for i := 0; i < count; i++ {
|
|
start := offset + (i * LEASE_SIZE)
|
|
end := start + LEASE_SIZE
|
|
lease_set_len := len(lease_set)
|
|
if lease_set_len < end {
|
|
log.WithFields(logrus.Fields{
|
|
"at": "(LeaseSet) Leases",
|
|
"data_len": lease_set_len,
|
|
"required_len": end,
|
|
"reason": "some leases missing",
|
|
}).Error("error parsnig lease set")
|
|
err = oops.Errorf("error parsing lease set: some leases missing")
|
|
return
|
|
}
|
|
var lease Lease
|
|
copy(lease[:], lease_set[start:end])
|
|
leases = append(leases, lease)
|
|
}
|
|
log.WithField("lease_count", len(leases)).Debug("Retrieved Leases from LeaseSet")
|
|
return
|
|
}
|
|
|
|
// Signature returns the signature as Signature.
|
|
// returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) Signature() (signature signature.Signature, err error) {
|
|
log.Debug("Retrieving Signature from LeaseSet")
|
|
destination, err := lease_set.Destination()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve Destination for Signature")
|
|
return
|
|
}
|
|
lease_count, err := lease_set.LeaseCount()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve LeaseCount for Signature")
|
|
return
|
|
}
|
|
start := len(destination.Bytes()) +
|
|
LEASE_SET_PUBKEY_SIZE +
|
|
LEASE_SET_SPK_SIZE +
|
|
1 +
|
|
(LEASE_SIZE * lease_count)
|
|
cert := destination.Certificate()
|
|
cert_type := cert.Type()
|
|
var end int
|
|
if cert_type == CERT_KEY {
|
|
keyCert, err := KeyCertificateFromCertificate(cert)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to create keyCert")
|
|
}
|
|
end = start + keyCert.SignatureSize()
|
|
} else {
|
|
end = start + LEASE_SET_SIG_SIZE
|
|
}
|
|
lease_set_len := len(lease_set)
|
|
if lease_set_len < end {
|
|
log.WithFields(logrus.Fields{
|
|
"at": "(LeaseSet) Signature",
|
|
"data_len": lease_set_len,
|
|
"required_len": end,
|
|
"reason": "not enough data",
|
|
}).Error("error parsing signatre")
|
|
err = oops.Errorf("error parsing signature: not enough data")
|
|
return
|
|
}
|
|
signature = []byte(lease_set[start:end])
|
|
log.WithField("signature_length", len(signature)).Debug("Retrieved Signature from LeaseSet")
|
|
return
|
|
}
|
|
|
|
// Verify returns nil
|
|
func (lease_set LeaseSet) Verify() error {
|
|
log.Debug("Verifying LeaseSet")
|
|
//data_end := len(destination) +
|
|
// LEASE_SET_PUBKEY_SIZE +
|
|
// LEASE_SET_SPK_SIZE +
|
|
// 1 +
|
|
// (44 * lease_set.LeaseCount())
|
|
//data := lease_set[:data_end]
|
|
//spk, _ := lease_set.
|
|
// Destination().
|
|
// signingPublicKey()
|
|
//verifier, err := spk.NewVerifier()
|
|
//if err != nil {
|
|
// return err
|
|
//}
|
|
log.Warn("LeaseSet verification not implemented")
|
|
return nil // verifier.Verify(data, lease_set.Signature())
|
|
}
|
|
|
|
// NewestExpiration returns the newest lease expiration as an I2P Date.
|
|
// Returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) NewestExpiration() (newest Date, err error) {
|
|
log.Debug("Finding newest expiration in LeaseSet")
|
|
leases, err := lease_set.Leases()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve Leases for NewestExpiration")
|
|
return
|
|
}
|
|
newest = Date{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
|
for _, lease := range leases {
|
|
date := lease.Date()
|
|
if date.Time().After(newest.Time()) {
|
|
newest = date
|
|
}
|
|
}
|
|
log.WithField("newest_expiration", newest.Time()).Debug("Found newest expiration in LeaseSet")
|
|
return
|
|
}
|
|
|
|
// OldestExpiration returns the oldest lease expiration as an I2P Date.
|
|
// Returns errors encountered during parsing.
|
|
func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
|
|
log.Debug("Finding oldest expiration in LeaseSet")
|
|
leases, err := lease_set.Leases()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to retrieve Leases for OldestExpiration")
|
|
return
|
|
}
|
|
earliest = Date{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
|
for _, lease := range leases {
|
|
date := lease.Date()
|
|
if date.Time().Before(earliest.Time()) {
|
|
earliest = date
|
|
}
|
|
}
|
|
log.WithField("oldest_expiration", earliest.Time()).Debug("Found oldest expiration in LeaseSet")
|
|
return
|
|
}
|
|
|
|
func NewLeaseSet(
|
|
destination Destination,
|
|
encryptionKey crypto.RecievingPublicKey,
|
|
signingKey crypto.SigningPublicKey,
|
|
leases []Lease,
|
|
signingPrivateKey crypto.SigningPrivateKey,
|
|
) (LeaseSet, error) {
|
|
log.Debug("Creating new LeaseSet")
|
|
// Validate destination size
|
|
if len(destination.KeysAndCert.Bytes()) < 387 {
|
|
return nil, oops.Errorf("invalid destination: minimum size is 387 bytes")
|
|
}
|
|
// Validate encryption key size
|
|
if len(encryptionKey.Bytes()) != LEASE_SET_PUBKEY_SIZE {
|
|
return nil, oops.Errorf("invalid encryption key size")
|
|
}
|
|
// Validate inputs
|
|
if len(leases) > 16 {
|
|
return nil, oops.Errorf("invalid lease set: more than 16 leases")
|
|
}
|
|
// Validate signing key size matches certificate
|
|
cert := destination.Certificate()
|
|
if cert.Type() == CERT_KEY {
|
|
// Get expected size from key certificate
|
|
keyCert, err := KeyCertificateFromCertificate(cert)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to create keyCert")
|
|
}
|
|
expectedSize := keyCert.SignatureSize()
|
|
if len(signingKey.Bytes()) != expectedSize {
|
|
return nil, oops.Errorf("invalid signing key size: got %d, expected %d",
|
|
len(signingKey.Bytes()), expectedSize)
|
|
}
|
|
} else {
|
|
// Default DSA size
|
|
if len(signingKey.Bytes()) != LEASE_SET_SPK_SIZE {
|
|
return nil, oops.Errorf("invalid signing key size")
|
|
}
|
|
}
|
|
// Build LeaseSet data
|
|
data := make([]byte, 0)
|
|
|
|
// Add Destination
|
|
data = append(data, destination.KeysAndCert.Bytes()...)
|
|
|
|
// Add encryption key
|
|
data = append(data, encryptionKey.Bytes()...)
|
|
|
|
// Add signing key
|
|
data = append(data, signingKey.Bytes()...)
|
|
|
|
// Add lease count
|
|
leaseCount, err := NewIntegerFromInt(len(leases), 1)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to create lease count")
|
|
return nil, err
|
|
}
|
|
data = append(data, leaseCount.Bytes()...)
|
|
|
|
// Add leases
|
|
for _, lease := range leases {
|
|
data = append(data, lease[:]...)
|
|
}
|
|
|
|
// Create signature for all data up to this point
|
|
signer, err := signingPrivateKey.NewSigner()
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to create signer")
|
|
return nil, err
|
|
}
|
|
|
|
signature, err := signer.Sign(data)
|
|
if err != nil {
|
|
log.WithError(err).Error("Failed to sign LeaseSet")
|
|
return nil, err
|
|
}
|
|
|
|
// Add signature
|
|
data = append(data, signature...)
|
|
|
|
log.WithFields(logrus.Fields{
|
|
"destination_length": len(destination.KeysAndCert.Bytes()),
|
|
"encryption_key_length": len(encryptionKey.Bytes()),
|
|
"signing_key_length": len(signingKey.Bytes()),
|
|
"lease_count": len(leases),
|
|
"total_length": len(data),
|
|
}).Debug("Successfully created new LeaseSet")
|
|
|
|
return LeaseSet(data), nil
|
|
}
|