2016-01-29 07:35:17 -05:00
|
|
|
package common
|
2016-01-28 12:58:58 -05:00
|
|
|
|
2016-02-14 22:28:20 -08:00
|
|
|
/*
|
|
|
|
I2P Certificate
|
2016-06-16 23:17:21 -07:00
|
|
|
https://geti2p.net/spec/common-structures#certificate
|
2016-02-14 22:28:20 -08:00
|
|
|
Accurate for version 0.9.24
|
|
|
|
|
|
|
|
+----+----+----+----+----+-//
|
|
|
|
|type| length | payload
|
|
|
|
+----+----+----+----+----+-//
|
|
|
|
|
|
|
|
type :: Integer
|
|
|
|
length -> 1 byte
|
|
|
|
|
|
|
|
case 0 -> NULL
|
|
|
|
case 1 -> HASHCASH
|
|
|
|
case 2 -> HIDDEN
|
|
|
|
case 3 -> SIGNED
|
|
|
|
case 4 -> MULTIPLE
|
|
|
|
case 5 -> KEY
|
|
|
|
|
|
|
|
length :: Integer
|
|
|
|
length -> 2 bytes
|
|
|
|
|
|
|
|
payload :: data
|
|
|
|
length -> $length bytes
|
|
|
|
*/
|
|
|
|
|
2016-01-28 12:58:58 -05:00
|
|
|
import (
|
2022-04-27 10:48:59 -04:00
|
|
|
"fmt"
|
|
|
|
|
2021-04-19 20:43:37 -04:00
|
|
|
log "github.com/sirupsen/logrus"
|
2022-05-22 19:59:20 -04:00
|
|
|
|
|
|
|
. "github.com/go-i2p/go-i2p/lib/common/data"
|
2016-01-28 12:58:58 -05:00
|
|
|
)
|
|
|
|
|
2016-02-13 21:00:29 -08:00
|
|
|
// Certificate Types
|
2016-01-28 12:58:58 -05:00
|
|
|
const (
|
2016-01-29 07:22:31 -05:00
|
|
|
CERT_NULL = iota
|
|
|
|
CERT_HASHCASH
|
|
|
|
CERT_HIDDEN
|
|
|
|
CERT_SIGNED
|
|
|
|
CERT_MULTIPLE
|
|
|
|
CERT_KEY
|
2016-01-28 12:58:58 -05:00
|
|
|
)
|
|
|
|
|
2016-06-17 21:07:16 -07:00
|
|
|
// Minimum size of a valid Certificate
|
2016-02-14 22:28:20 -08:00
|
|
|
const (
|
2016-02-16 01:04:40 -08:00
|
|
|
CERT_MIN_SIZE = 3
|
2016-02-14 22:28:20 -08:00
|
|
|
)
|
|
|
|
|
2022-04-27 10:48:59 -04:00
|
|
|
type Certificate struct {
|
|
|
|
kind Integer
|
|
|
|
leng Integer
|
|
|
|
payl []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Certificate) RawBytes() []byte {
|
|
|
|
bytes := c.kind.Bytes()
|
|
|
|
bytes = append(bytes, c.leng.Bytes()...)
|
|
|
|
bytes = append(bytes, c.payl...)
|
|
|
|
return bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Certificate) ExcessBytes() []byte {
|
|
|
|
return c.payl[c.leng.Int():]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Certificate) Bytes() []byte {
|
|
|
|
bytes := c.kind.Bytes()
|
|
|
|
bytes = append(bytes, c.leng.Bytes()...)
|
|
|
|
bytes = append(bytes, c.Data()...)
|
|
|
|
return bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Certificate) length() (cert_len int) {
|
|
|
|
cert_len = len(c.Bytes())
|
|
|
|
return
|
|
|
|
}
|
2016-01-28 12:58:58 -05:00
|
|
|
|
2016-02-14 22:28:20 -08:00
|
|
|
//
|
|
|
|
// Return the Certificate Type specified in the first byte of the Certificate,
|
|
|
|
// and an error if the certificate is shorter than the minimum certificate size.
|
|
|
|
//
|
2022-04-27 10:48:59 -04:00
|
|
|
func (c *Certificate) Type() (cert_type int) {
|
|
|
|
cert_type = c.kind.Int()
|
2016-02-13 21:00:29 -08:00
|
|
|
return
|
2016-01-28 12:58:58 -05:00
|
|
|
}
|
|
|
|
|
2016-02-06 01:42:47 -08:00
|
|
|
//
|
2016-02-14 22:28:20 -08:00
|
|
|
// Look up the length of the Certificate, reporting errors if the certificate is
|
|
|
|
// shorter than the minimum certificate size or if the reported length doesn't
|
|
|
|
// match the provided data.
|
2016-02-06 01:42:47 -08:00
|
|
|
//
|
2022-04-27 10:48:59 -04:00
|
|
|
func (c *Certificate) Length() (length int) {
|
|
|
|
length = c.leng.Int()
|
2016-02-14 22:28:20 -08:00
|
|
|
return
|
2016-01-28 12:58:58 -05:00
|
|
|
}
|
|
|
|
|
2016-02-06 01:42:47 -08:00
|
|
|
//
|
2016-02-14 22:28:20 -08:00
|
|
|
// Return the Certificate data and any errors encountered parsing the Certificate.
|
2016-02-06 01:42:47 -08:00
|
|
|
//
|
2022-04-27 10:48:59 -04:00
|
|
|
func (c *Certificate) Data() (data []byte) {
|
|
|
|
lastElement := c.Length()
|
|
|
|
if lastElement > len(c.payl) {
|
|
|
|
data = c.payl
|
|
|
|
} else {
|
|
|
|
data = c.payl[0:lastElement]
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCertificate(data []byte) (certificate *Certificate, err error) {
|
|
|
|
certificate = &Certificate{}
|
|
|
|
switch len(data) {
|
|
|
|
case 0:
|
2022-05-09 20:43:24 -04:00
|
|
|
certificate.kind = Integer([]byte{0})
|
|
|
|
certificate.leng = Integer([]byte{0})
|
2022-04-27 10:48:59 -04:00
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"at": "(Certificate) NewCertificate",
|
|
|
|
"certificate_bytes_length": len(data),
|
|
|
|
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
|
|
|
|
}).Error("invalid certificate")
|
|
|
|
err = fmt.Errorf("error parsing certificate: certificate is too short")
|
|
|
|
return
|
|
|
|
case 1:
|
2022-05-09 20:43:24 -04:00
|
|
|
certificate.kind = Integer(data[0:0])
|
|
|
|
certificate.leng = Integer([]byte{0})
|
2022-04-27 10:48:59 -04:00
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"at": "(Certificate) NewCertificate",
|
|
|
|
"certificate_bytes_length": len(data),
|
|
|
|
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
|
|
|
|
}).Error("invalid certificate")
|
|
|
|
err = fmt.Errorf("error parsing certificate: certificate is too short")
|
|
|
|
return
|
|
|
|
case 2:
|
2022-05-09 20:43:24 -04:00
|
|
|
certificate.kind = Integer(data[0:1])
|
|
|
|
certificate.leng = Integer([]byte{0})
|
2022-04-27 10:48:59 -04:00
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"at": "(Certificate) NewCertificate",
|
|
|
|
"certificate_bytes_length": len(data),
|
|
|
|
"reason": "too short (len < CERT_MIN_SIZE)" + fmt.Sprintf("%d", certificate.kind.Int()),
|
|
|
|
}).Error("invalid certificate")
|
|
|
|
err = fmt.Errorf("error parsing certificate length: certificate is too short")
|
|
|
|
return
|
|
|
|
default:
|
2022-05-09 20:43:24 -04:00
|
|
|
certificate.kind = Integer(data[0:1])
|
|
|
|
certificate.leng = Integer(data[1:3])
|
2022-04-27 10:48:59 -04:00
|
|
|
payleng := len(data) - CERT_MIN_SIZE
|
|
|
|
certificate.payl = data[CERT_MIN_SIZE:]
|
|
|
|
if certificate.leng.Int() > len(data)-CERT_MIN_SIZE {
|
|
|
|
err = fmt.Errorf("certificate parsing warning: certificate data is shorter than specified by length")
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"at": "(Certificate) NewCertificate",
|
|
|
|
"certificate_bytes_length": certificate.leng.Int(),
|
|
|
|
"certificate_payload_length": payleng,
|
|
|
|
"reason": err.Error(),
|
|
|
|
}).Error("invalid certificate")
|
2016-02-14 22:28:20 -08:00
|
|
|
return
|
2022-04-27 10:48:59 -04:00
|
|
|
} else if certificate.leng.Int() < len(data)-CERT_MIN_SIZE {
|
|
|
|
err = fmt.Errorf("certificate parsing warning: certificate data is longer than specified by length")
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"at": "(Certificate) NewCertificate",
|
|
|
|
"certificate_bytes_length": certificate.leng.Int(),
|
|
|
|
"certificate_payload_length": payleng,
|
|
|
|
"reason": err.Error(),
|
|
|
|
}).Error("invalid certificate")
|
2016-02-14 22:28:20 -08:00
|
|
|
return
|
2016-02-05 02:23:11 -08:00
|
|
|
}
|
2022-04-27 10:48:59 -04:00
|
|
|
return
|
2016-01-29 07:22:31 -05:00
|
|
|
}
|
2016-01-28 12:58:58 -05:00
|
|
|
}
|
2016-02-01 01:56:10 -08:00
|
|
|
|
2016-02-06 01:42:47 -08:00
|
|
|
//
|
2016-02-14 22:28:20 -08:00
|
|
|
// Read a Certificate from a slice of bytes, returning any extra data on the end of the slice
|
|
|
|
// and any errors if a valid Certificate could not be read.
|
2016-02-06 01:42:47 -08:00
|
|
|
//
|
2022-04-27 10:48:59 -04:00
|
|
|
func ReadCertificate(data []byte) (certificate *Certificate, remainder []byte, err error) {
|
|
|
|
certificate, err = NewCertificate(data)
|
|
|
|
if err != nil && err.Error() == "certificate parsing warning: certificate data is longer than specified by length" {
|
|
|
|
remainder = certificate.ExcessBytes()
|
2016-02-14 22:28:20 -08:00
|
|
|
err = nil
|
2016-02-05 02:23:11 -08:00
|
|
|
}
|
2016-02-14 22:28:20 -08:00
|
|
|
return
|
2016-02-01 01:56:10 -08:00
|
|
|
}
|