Files
go-i2p/lib/common/lease_set/lease_set.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
}