140 lines
3.8 KiB
Go
140 lines
3.8 KiB
Go
package common
|
|
|
|
/*
|
|
I2P Certificate
|
|
https://geti2p.net/spec/common-structures#certificate
|
|
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
|
|
*/
|
|
|
|
import (
|
|
"errors"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Certificate Types
|
|
const (
|
|
CERT_NULL = iota
|
|
CERT_HASHCASH
|
|
CERT_HIDDEN
|
|
CERT_SIGNED
|
|
CERT_MULTIPLE
|
|
CERT_KEY
|
|
)
|
|
|
|
// Minimum size of a valid Certificate
|
|
const (
|
|
CERT_MIN_SIZE = 3
|
|
)
|
|
|
|
type Certificate []byte
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
func (certificate Certificate) Type() (cert_type int, err error) {
|
|
cert_len := len(certificate)
|
|
if cert_len < CERT_MIN_SIZE {
|
|
log.WithFields(log.Fields{
|
|
"at": "(Certificate) Type",
|
|
"certificate_bytes_length": cert_len,
|
|
"reason": "too short (len < CERT_MIN_SIZE)",
|
|
}).Error("invalid certificate")
|
|
err = errors.New("error parsing certificate length: certificate is too short")
|
|
return
|
|
}
|
|
cert_type = Integer([]byte{certificate[0]})
|
|
return
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
func (certificate Certificate) Length() (length int, err error) {
|
|
cert_len := len(certificate)
|
|
_, err = certificate.Type()
|
|
if err != nil {
|
|
return
|
|
}
|
|
length = Integer(certificate[1:CERT_MIN_SIZE])
|
|
inferred_len := length + CERT_MIN_SIZE
|
|
if inferred_len > cert_len {
|
|
log.WithFields(log.Fields{
|
|
"at": "(Certificate) Length",
|
|
"certificate_bytes_length": cert_len,
|
|
"certificate_length_field": length,
|
|
"expected_bytes_length": inferred_len,
|
|
"reason": "data shorter than specified",
|
|
}).Warn("certificate format warning")
|
|
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
|
} else if cert_len > inferred_len {
|
|
log.WithFields(log.Fields{
|
|
"at": "(Certificate) Length",
|
|
"certificate_bytes_length": cert_len,
|
|
"certificate_length_field": length,
|
|
"expected_bytes_length": inferred_len,
|
|
"reason": "data longer than expected",
|
|
}).Warn("certificate format warning")
|
|
err = errors.New("certificate parsing warning: certificate contains data beyond length")
|
|
}
|
|
return
|
|
}
|
|
|
|
//
|
|
// Return the Certificate data and any errors encountered parsing the Certificate.
|
|
//
|
|
func (certificate Certificate) Data() (data []byte, err error) {
|
|
length, err := certificate.Length()
|
|
if err != nil {
|
|
switch err.Error() {
|
|
case "error parsing certificate length: certificate is too short":
|
|
return
|
|
case "certificate parsing warning: certificate data is shorter than specified by length":
|
|
data = certificate[CERT_MIN_SIZE:]
|
|
return
|
|
case "certificate parsing warning: certificate contains data beyond length":
|
|
data = certificate[CERT_MIN_SIZE : length+CERT_MIN_SIZE]
|
|
return
|
|
}
|
|
}
|
|
data = certificate[CERT_MIN_SIZE:]
|
|
return
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
|
|
certificate = Certificate(data)
|
|
length, err := certificate.Length()
|
|
if err != nil && err.Error() == "certificate parsing warning: certificate contains data beyond length" {
|
|
certificate = Certificate(data[:length+CERT_MIN_SIZE])
|
|
remainder = data[length+CERT_MIN_SIZE:]
|
|
err = nil
|
|
}
|
|
return
|
|
}
|