Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
4f19c48da3 | |||
961dfe4266 | |||
afb38b6165 | |||
0aa32b4aad | |||
3adf694c25 | |||
a11c3b73cb | |||
853bc79f8f | |||
77f1c6dd0a | |||
ee7d8a0d63 | |||
c253bf31ac | |||
b97b2854c1 | |||
d5266f8980 | |||
6de4dde1f2 | |||
648c05b15f | |||
bfc7237ba6 | |||
ffbdc7f967 | |||
1cd9d16760 | |||
896df4e483 | |||
49d7eeb441 | |||
7893694c91 | |||
1ea426da9c |
3
Makefile
3
Makefile
@ -23,3 +23,6 @@ test:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(GO) clean -v
|
$(GO) clean -v
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
find . -name '*.go' -exec gofmt -w -s {} \;
|
61
README.md
61
README.md
@ -4,10 +4,35 @@ A pure Go implementation of the I2P router.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
go-i2p is in early development.
|
go-i2p was in early development. Now it's being restructured in some
|
||||||
|
fundamental ways, so it's even less done than before(on this branch, for now)
|
||||||
|
but when this restructuring is complete, it will be a fully-fledged I2P router
|
||||||
|
and library for writing, embedding, and possiblly extending I2P routers in Go
|
||||||
|
applications.
|
||||||
|
|
||||||
|
The go module is declared as: `github.com/go-i2p/go-i2p`, in order to clone
|
||||||
|
anonymously you may use `torsocks` with `go get`(YMMV) or you may clone
|
||||||
|
it from git.idk.i2p using:
|
||||||
|
|
||||||
|
#Set your $GOPATH, if it isn't set already then GOPATH=$HOME/go
|
||||||
|
$GOPATH/go/src/i2pgit.org/idk/
|
||||||
|
git clone git@127.0.0.1:idk/go-i2p $GOPATH/go/src/github.com/go-i2p/go-i2p
|
||||||
|
$GOPATH/go/src/github.com/go-i2p/go-i2p
|
||||||
|
|
||||||
|
And build with `GO111MODULES=off` or use a `replace` directive in your `go.mod`
|
||||||
|
to direct to the local module source. Or you may run your own Go Modules proxy as
|
||||||
|
a hidden service. I'll make this about a billion times easier in the near future I
|
||||||
|
promise.
|
||||||
|
|
||||||
### Implemented Features
|
### Implemented Features
|
||||||
|
|
||||||
|
As the application is restructured and moved away from representing I2P data
|
||||||
|
structures as byte slices, this chart will be filled in, when the tests pass,
|
||||||
|
the item will be checked off. Currently, much of this is partially implemented
|
||||||
|
in byte-slice versions and partially implemented as Go Structs. Very little of
|
||||||
|
it will work until it's all moved to Go Structs where appropriate. Most of
|
||||||
|
this will happen in /lib/common.
|
||||||
|
|
||||||
- Cryptographic primitives
|
- Cryptographic primitives
|
||||||
- Signing
|
- Signing
|
||||||
- [ ] ECDSA_SHA256_P256
|
- [ ] ECDSA_SHA256_P256
|
||||||
@ -23,8 +48,38 @@ go-i2p is in early development.
|
|||||||
- [ ] RSA_SHA384_3072
|
- [ ] RSA_SHA384_3072
|
||||||
- [ ] RSA_SHA512_4096
|
- [ ] RSA_SHA512_4096
|
||||||
- [ ] Ed25519
|
- [ ] Ed25519
|
||||||
- [ ] ElGamal
|
- [x] ElGamal
|
||||||
- [ ] AES256
|
- [x] AES256
|
||||||
|
- Common Structures
|
||||||
|
- Common Type Specification
|
||||||
|
- [x] Integer
|
||||||
|
- [x] Date
|
||||||
|
- [x] String
|
||||||
|
- [x] PublicKey* As interface in lib/crypto
|
||||||
|
- [x] PrivateKey* As interface in lib/crypto
|
||||||
|
- [ ] SessionKey
|
||||||
|
- [ ] SigningPublicKey
|
||||||
|
- [ ] Signature
|
||||||
|
- [x] Hash
|
||||||
|
- [ ] Session Tag
|
||||||
|
- [ ] Tunnel ID
|
||||||
|
- [x] Certificate
|
||||||
|
- [ ] Mapping
|
||||||
|
- Common Structure Specification
|
||||||
|
- [ ] KeysAndCert
|
||||||
|
- [ ] RouterIdentity
|
||||||
|
- [ ] Destination
|
||||||
|
- [ ] Lease
|
||||||
|
- [ ] LeaseSet
|
||||||
|
- [ ] Lease2
|
||||||
|
- [ ] OfflineSigntature
|
||||||
|
- [ ] LeaseSet2Header
|
||||||
|
- [ ] LeaseSet2
|
||||||
|
- [ ] MetaLease
|
||||||
|
- [ ] MetaLeaseSet
|
||||||
|
- [ ] EncryptedLeaseSet
|
||||||
|
- [ ] RouterAddress
|
||||||
|
- [ ] RouterInfo
|
||||||
- I2NP
|
- I2NP
|
||||||
- [ ] Message parsing
|
- [ ] Message parsing
|
||||||
- [ ] Message handling
|
- [ ] Message handling
|
||||||
|
@ -46,25 +46,45 @@ const (
|
|||||||
CERT_MIN_SIZE = 3
|
CERT_MIN_SIZE = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
type Certificate []byte
|
type CertificateInterface interface {
|
||||||
|
Cert() []byte
|
||||||
|
Length() (length int, err error)
|
||||||
|
Data() (data []byte, err error)
|
||||||
|
Type() (cert_type int, err error)
|
||||||
|
SignatureSize() (size int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Certificate struct {
|
||||||
|
CertType int
|
||||||
|
CertLen int
|
||||||
|
CertBytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var ci CertificateInterface = &Certificate{}
|
||||||
|
|
||||||
|
func (certificate Certificate) SignatureSize() (size int) {
|
||||||
|
return 40
|
||||||
|
}
|
||||||
|
|
||||||
|
func (certificate Certificate) Cert() []byte {
|
||||||
|
var ret []byte
|
||||||
|
ret = append(ret, IntegerBytes(certificate.CertType)...)
|
||||||
|
data, _ := certificate.Data()
|
||||||
|
if certificate.CertLen != 0 && len(data) != 0 {
|
||||||
|
ret = append(ret, LengthBytes(certificate.CertLen)...)
|
||||||
|
ret = append(ret, data...)
|
||||||
|
} else {
|
||||||
|
ret = append(ret, IntegerBytes(certificate.CertLen)...)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the Certificate Type specified in the first byte of the Certificate,
|
// 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.
|
// and an error if the certificate is shorter than the minimum certificate size.
|
||||||
//
|
//
|
||||||
func (certificate Certificate) Type() (cert_type int, err error) {
|
func (certificate Certificate) Type() (cert_type int, err error) {
|
||||||
cert_len := len(certificate)
|
return certificate.CertType, nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -73,31 +93,39 @@ func (certificate Certificate) Type() (cert_type int, err error) {
|
|||||||
// match the provided data.
|
// match the provided data.
|
||||||
//
|
//
|
||||||
func (certificate Certificate) Length() (length int, err error) {
|
func (certificate Certificate) Length() (length int, err error) {
|
||||||
cert_len := len(certificate)
|
if certificate.CertLen < 1 {
|
||||||
_, 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{
|
log.WithFields(log.Fields{
|
||||||
"at": "(Certificate) Length",
|
"at": "(Certificate) Length",
|
||||||
"certificate_bytes_length": cert_len,
|
"certificate_bytes_length": certificate.CertLen,
|
||||||
"certificate_length_field": length,
|
"certificate_min_size": CERT_MIN_SIZE - 1,
|
||||||
"expected_bytes_length": inferred_len,
|
"reason": "certificate is too short",
|
||||||
"reason": "data shorter than specified",
|
}).Warn("certificate format warning")
|
||||||
|
err = errors.New("error parsing certificate length: certificate is too short")
|
||||||
|
}
|
||||||
|
if certificate.CertLen > len(certificate.CertBytes) {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(Certificate) Length",
|
||||||
|
"certificate_bytes_length": certificate.CertLen,
|
||||||
|
"certificate_actual_length": len(certificate.CertBytes),
|
||||||
|
"reason": "certificate data is shorter than specified by length",
|
||||||
}).Warn("certificate format warning")
|
}).Warn("certificate format warning")
|
||||||
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
||||||
} else if cert_len > inferred_len {
|
length = len(certificate.CertBytes)
|
||||||
|
}
|
||||||
|
if certificate.CertLen < len(certificate.CertBytes) {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"at": "(Certificate) Length",
|
"at": "(Certificate) Length",
|
||||||
"certificate_bytes_length": cert_len,
|
"certificate_bytes_length": certificate.CertLen,
|
||||||
"certificate_length_field": length,
|
"certificate_actual_length": len(certificate.CertBytes),
|
||||||
"expected_bytes_length": inferred_len,
|
"reason": "certificate contains data beyond length",
|
||||||
"reason": "data longer than expected",
|
|
||||||
}).Warn("certificate format warning")
|
}).Warn("certificate format warning")
|
||||||
err = errors.New("certificate parsing warning: certificate contains data beyond length")
|
err = errors.New("certificate parsing warning: certificate contains data beyond length")
|
||||||
|
length = certificate.CertLen
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length = certificate.CertLen
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -106,20 +134,20 @@ func (certificate Certificate) Length() (length int, err error) {
|
|||||||
// Return the Certificate data and any errors encountered parsing the Certificate.
|
// Return the Certificate data and any errors encountered parsing the Certificate.
|
||||||
//
|
//
|
||||||
func (certificate Certificate) Data() (data []byte, err error) {
|
func (certificate Certificate) Data() (data []byte, err error) {
|
||||||
length, err := certificate.Length()
|
_, err = certificate.Length()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.Error() {
|
switch err.Error() {
|
||||||
case "error parsing certificate length: certificate is too short":
|
case "error parsing certificate length: certificate is too short":
|
||||||
return
|
return
|
||||||
case "certificate parsing warning: certificate data is shorter than specified by length":
|
case "certificate parsing warning: certificate data is shorter than specified by length":
|
||||||
data = certificate[CERT_MIN_SIZE:]
|
data = certificate.CertBytes
|
||||||
return
|
return
|
||||||
case "certificate parsing warning: certificate contains data beyond length":
|
case "certificate parsing warning: certificate contains data beyond length":
|
||||||
data = certificate[CERT_MIN_SIZE : length+CERT_MIN_SIZE]
|
data = certificate.CertBytes[:certificate.CertLen]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data = certificate[CERT_MIN_SIZE:]
|
data = certificate.CertBytes
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,12 +156,41 @@ func (certificate Certificate) Data() (data []byte, err error) {
|
|||||||
// and any errors if a valid Certificate could not be read.
|
// and any errors if a valid Certificate could not be read.
|
||||||
//
|
//
|
||||||
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
|
func ReadCertificate(data []byte) (certificate Certificate, remainder []byte, err error) {
|
||||||
certificate = Certificate(data)
|
certificate.CertType = Integer(data[0:1])
|
||||||
length, err := certificate.Length()
|
cert_len := len(data)
|
||||||
if err != nil && err.Error() == "certificate parsing warning: certificate contains data beyond length" {
|
|
||||||
certificate = Certificate(data[:length+CERT_MIN_SIZE])
|
if cert_len < CERT_MIN_SIZE {
|
||||||
remainder = data[length+CERT_MIN_SIZE:]
|
log.WithFields(log.Fields{
|
||||||
err = nil
|
"at": "(Certificate) ReadCertificate",
|
||||||
|
"certificate_bytes_length": cert_len,
|
||||||
|
"certificate_min_size": CERT_MIN_SIZE,
|
||||||
|
"reason": "certificate is too short",
|
||||||
|
}).Warn("certificate format warning")
|
||||||
|
err = errors.New("error parsing certificate length: certificate is too short")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
certificate.CertLen = Integer(data[1:CERT_MIN_SIZE])
|
||||||
|
_, err = certificate.Type()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certificate.CertBytes = data[CERT_MIN_SIZE:]
|
||||||
|
_, 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":
|
||||||
|
//err = nil
|
||||||
|
return
|
||||||
|
case "certificate parsing warning: certificate contains data beyond length":
|
||||||
|
certificate.CertBytes = data[CERT_MIN_SIZE:]
|
||||||
|
remainder = data[CERT_MIN_SIZE+certificate.CertLen:]
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,10 @@ func TestCertificateTypeIsFirstByte(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x00}
|
bytes := []byte{0x03, 0x00, 0x00}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
cert_type, err := certificate.Type()
|
cert_type, err := certificate.Type()
|
||||||
|
|
||||||
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
|
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
|
||||||
@ -20,7 +23,7 @@ func TestCertificateLengthCorrect(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_len, err := certificate.Length()
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
|
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
|
||||||
@ -31,7 +34,10 @@ func TestCertificateLengthErrWhenTooShort(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x01}
|
bytes := []byte{0x03, 0x01}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
|
if assert.NotNil(err) {
|
||||||
|
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
||||||
|
}
|
||||||
cert_len, err := certificate.Length()
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
|
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
|
||||||
@ -44,7 +50,7 @@ func TestCertificateLengthErrWhenDataTooShort(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_len, err := certificate.Length()
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
|
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
|
||||||
@ -57,28 +63,31 @@ func TestCertificateDataWhenCorrectSize(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
|
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_data, err := certificate.Data()
|
assert.Nil(err, "certificate.Data() returned error with valid data")
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
assert.Nil(err, "certificate.Data() returned error with valid data")
|
assert.Nil(err, "certificate.Data() returned error with valid data")
|
||||||
cert_len := len(cert_data)
|
|
||||||
assert.Equal(cert_len, 1, "certificate.Length() did not return indicated length when data was valid")
|
assert.Equal(cert_len, 1, "certificate.Length() did not return indicated length when data was valid")
|
||||||
assert.Equal(170, int(cert_data[0]), "certificate.Data() returned incorrect data")
|
data := Integer(certificate.CertBytes)
|
||||||
|
assert.Equal(170, data, "certificate.Data() returned incorrect data")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCertificateDataWhenTooLong(t *testing.T) {
|
func TestCertificateDataWhenTooLong(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_data, err := certificate.Data()
|
|
||||||
|
cert_len, err := certificate.Length()
|
||||||
|
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("certificate parsing warning: certificate contains data beyond length", err.Error(), "correct error message should be returned")
|
assert.Equal("certificate parsing warning: certificate contains data beyond length", err.Error(), "correct error message should be returned")
|
||||||
}
|
}
|
||||||
cert_len := len(cert_data)
|
|
||||||
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was too long")
|
assert.Equal(cert_len, 2, "certificate.Length() did not return indicated length when data was too long")
|
||||||
if cert_data[0] != 0xff || cert_data[1] != 0xff {
|
if certificate.CertBytes[0] != 0xff || certificate.CertBytes[1] != 0xff {
|
||||||
t.Fatal("certificate.Data() returned incorrect data when data was too long")
|
t.Fatal("certificate.Data() returned incorrect data when data was too long")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +96,7 @@ func TestCertificateDataWhenTooShort(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
||||||
certificate := Certificate(bytes)
|
certificate, _, err := ReadCertificate(bytes)
|
||||||
cert_data, err := certificate.Data()
|
cert_data, err := certificate.Data()
|
||||||
|
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
@ -104,7 +113,7 @@ func TestReadCertificateWithCorrectData(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
|
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(len(cert), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
|
assert.Equal(len(cert.Cert()), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
|
||||||
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on a valid certificate")
|
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on a valid certificate")
|
||||||
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||||
}
|
}
|
||||||
@ -115,7 +124,7 @@ func TestReadCertificateWithDataTooShort(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00, 0x02, 0xff}
|
bytes := []byte{0x00, 0x00, 0x02, 0xff}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(len(cert), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
|
assert.Equal(len(cert.Cert()), 4, "ReadCertificate() did not return correct amount of data for certificate with missing data")
|
||||||
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on certificate with missing data")
|
assert.Equal(len(remainder), 0, "ReadCertificate() did not return a zero length remainder on certificate with missing data")
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
|
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error(), "correct error message should be returned")
|
||||||
@ -128,7 +137,7 @@ func TestReadCertificateWithRemainder(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
|
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(len(cert), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
|
assert.Equal(len(cert.Cert()), 5, "ReadCertificate() did not return correct amount of data for certificate with extra data")
|
||||||
assert.Equal(len(remainder), 1, "ReadCertificate() returned incorrect length remainder on certificate with extra data")
|
assert.Equal(len(remainder), 1, "ReadCertificate() returned incorrect length remainder on certificate with extra data")
|
||||||
assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
|
assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
@ -140,7 +149,7 @@ func TestReadCertificateWithInvalidLength(t *testing.T) {
|
|||||||
bytes := []byte{0x00, 0x00}
|
bytes := []byte{0x00, 0x00}
|
||||||
cert, remainder, err := ReadCertificate(bytes)
|
cert, remainder, err := ReadCertificate(bytes)
|
||||||
|
|
||||||
assert.Equal(len(cert), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
|
assert.Equal(len(cert.Cert()), 2, "ReadCertificate() should populate the certificate with the provided data even when invalid")
|
||||||
assert.Equal(len(remainder), 0, "ReadCertificate() returned non-zero length remainder on invalid certificate")
|
assert.Equal(len(remainder), 0, "ReadCertificate() returned non-zero length remainder on invalid certificate")
|
||||||
if assert.NotNil(err) {
|
if assert.NotNil(err) {
|
||||||
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
||||||
|
@ -19,31 +19,27 @@ import (
|
|||||||
// A Destination is a KeysAndCert with functionallity
|
// A Destination is a KeysAndCert with functionallity
|
||||||
// for generating base32 and base64 addresses.
|
// for generating base32 and base64 addresses.
|
||||||
//
|
//
|
||||||
type Destination []byte
|
type Destination struct {
|
||||||
|
KeysAndCert
|
||||||
|
}
|
||||||
|
|
||||||
func (destination Destination) PublicKey() (crypto.PublicKey, error) {
|
func (destination Destination) PublicKey() (crypto.PublicKey, error) {
|
||||||
return KeysAndCert(destination).PublicKey()
|
return destination.KeysAndCert.GetPublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (destination Destination) SigningPublicKey() (crypto.SigningPublicKey, error) {
|
func (destination Destination) SigningPublicKey() (crypto.SigningPublicKey, error) {
|
||||||
return KeysAndCert(destination).SigningPublicKey()
|
return destination.KeysAndCert.GetSigningPublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (destination Destination) Certificate() (Certificate, error) {
|
func (destination Destination) Certificate() (CertificateInterface, error) {
|
||||||
return KeysAndCert(destination).Certificate()
|
return destination.KeysAndCert.GetCertificate()
|
||||||
}
|
|
||||||
|
|
||||||
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error) {
|
|
||||||
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
|
||||||
destination = Destination(keys_and_cert)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Generate the I2P base32 address for this Destination.
|
// Generate the I2P base32 address for this Destination.
|
||||||
//
|
//
|
||||||
func (destination Destination) Base32Address() (str string) {
|
func (destination Destination) Base32Address() (str string) {
|
||||||
hash := crypto.SHA256(destination)
|
hash := crypto.SHA256(destination.Cert())
|
||||||
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
|
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
|
||||||
str = str + ".b32.i2p"
|
str = str + ".b32.i2p"
|
||||||
return
|
return
|
||||||
@ -53,5 +49,14 @@ func (destination Destination) Base32Address() (str string) {
|
|||||||
// Generate the I2P base64 address for this Destination.
|
// Generate the I2P base64 address for this Destination.
|
||||||
//
|
//
|
||||||
func (destination Destination) Base64() string {
|
func (destination Destination) Base64() string {
|
||||||
return base64.EncodeToString(destination)
|
return base64.EncodeToString(destination.Cert())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error) {
|
||||||
|
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
destination.KeysAndCert = keys_and_cert
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
@ -30,3 +30,23 @@ func Integer(number []byte) (value int) {
|
|||||||
value = int(binary.BigEndian.Uint64(number))
|
value = int(binary.BigEndian.Uint64(number))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Take an int representation and return a big endian integer.
|
||||||
|
//
|
||||||
|
func IntegerBytes(value int) (number []byte) {
|
||||||
|
onumber := make([]byte, INTEGER_SIZE)
|
||||||
|
// var number []byte
|
||||||
|
binary.BigEndian.PutUint64(onumber, uint64(value))
|
||||||
|
var index int
|
||||||
|
for i, j := range onumber {
|
||||||
|
index = i
|
||||||
|
if j != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
number = onumber[index:]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -74,13 +74,25 @@ const (
|
|||||||
KEYCERT_SPK_SIZE = 128
|
KEYCERT_SPK_SIZE = 128
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeyCertificate []byte
|
type KeyCertificate struct {
|
||||||
|
Certificate
|
||||||
|
PKType int
|
||||||
|
PKExtra []byte
|
||||||
|
SPKType int
|
||||||
|
SPKExtra []byte
|
||||||
|
} //[]byte
|
||||||
|
|
||||||
//
|
//
|
||||||
// The data contained in the Key Certificate.
|
// The data contained in the Key Certificate.
|
||||||
//
|
//
|
||||||
func (key_certificate KeyCertificate) Data() ([]byte, error) {
|
func (key_certificate KeyCertificate) Data() ([]byte, error) {
|
||||||
return Certificate(key_certificate).Data()
|
var r []byte
|
||||||
|
r = append(r, key_certificate.Certificate.Cert()...)
|
||||||
|
pk := IntegerBytes(key_certificate.PKType)
|
||||||
|
r = append(r, pk...)
|
||||||
|
spk := IntegerBytes(key_certificate.SPKType)
|
||||||
|
r = append(r, spk...)
|
||||||
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -88,27 +100,7 @@ func (key_certificate KeyCertificate) Data() ([]byte, error) {
|
|||||||
// parsing the KeyCertificate.
|
// parsing the KeyCertificate.
|
||||||
//
|
//
|
||||||
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int, err error) {
|
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int, err error) {
|
||||||
data, err := key_certificate.Data()
|
return key_certificate.SPKType, nil
|
||||||
if err != nil {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"at": "(KeyCertificate) SigningPublicKeyType",
|
|
||||||
"reason": err.Error(),
|
|
||||||
}).Error("error getting signing public key")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data_len := len(data)
|
|
||||||
if data_len < 2 {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"at": "(KeyCertificate) SigningPublicKeyType",
|
|
||||||
"data_len": data_len,
|
|
||||||
"required_len": 2,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing key certificate")
|
|
||||||
err = errors.New("error parsing key certificate: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
signing_pubkey_type = Integer(data[:2])
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -116,23 +108,7 @@ func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_typ
|
|||||||
// this KeyCertificate.
|
// this KeyCertificate.
|
||||||
//
|
//
|
||||||
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err error) {
|
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err error) {
|
||||||
data, err := key_certificate.Data()
|
return key_certificate.PKType, nil
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data_len := len(data)
|
|
||||||
if data_len < 4 {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"at": "(KeyCertificate) PublicKeyType",
|
|
||||||
"data_len": data_len,
|
|
||||||
"required_len": 4,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing key certificate")
|
|
||||||
err = errors.New("error parsing key certificate: not enough data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pubkey_type = Integer(data[2:4])
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -201,7 +177,8 @@ func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (si
|
|||||||
var ec_key crypto.ECP521PublicKey
|
var ec_key crypto.ECP521PublicKey
|
||||||
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
|
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
|
||||||
copy(ec_key[:], data)
|
copy(ec_key[:], data)
|
||||||
copy(ec_key[KEYCERT_SPK_SIZE:], key_certificate[4:4+extra])
|
d, _ := key_certificate.Data()
|
||||||
|
copy(ec_key[KEYCERT_SPK_SIZE:], d[4:4+extra])
|
||||||
signing_public_key = ec_key
|
signing_public_key = ec_key
|
||||||
case KEYCERT_SIGN_RSA2048:
|
case KEYCERT_SIGN_RSA2048:
|
||||||
//var rsa_key crypto.RSA2048PublicKey
|
//var rsa_key crypto.RSA2048PublicKey
|
||||||
@ -244,3 +221,38 @@ func (key_certificate KeyCertificate) SignatureSize() (size int) {
|
|||||||
}
|
}
|
||||||
return sizes[int(key_type)]
|
return sizes[int(key_type)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read a KeyCertificate from a slice of bytes
|
||||||
|
//
|
||||||
|
func ReadKeyCertificate(data []byte) (key_certificate KeyCertificate, err error) {
|
||||||
|
cert, data, err := ReadCertificate(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key_certificate.Certificate = cert
|
||||||
|
data_len := len(data)
|
||||||
|
if data_len < 2 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) SigningPublicKeyType",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": 2,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing key certificate")
|
||||||
|
err = errors.New("error parsing key certificate: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key_certificate.SPKType = Integer(data[:2])
|
||||||
|
if data_len < 4 {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeyCertificate) PublicKeyType",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": 4,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing key certificate")
|
||||||
|
err = errors.New("error parsing key certificate: not enough data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key_certificate.PKType = Integer(data[2:4])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -59,14 +59,33 @@ const (
|
|||||||
KEYS_AND_CERT_DATA_SIZE = 384
|
KEYS_AND_CERT_DATA_SIZE = 384
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeysAndCert []byte
|
type KeysAndCertInterface interface {
|
||||||
|
GetPublicKey() (key crypto.PublicKey, err error)
|
||||||
|
GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error)
|
||||||
|
GetCertificate() (cert Certificate, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeysAndCert struct {
|
||||||
|
crypto.SigningPublicKey
|
||||||
|
crypto.PublicKey
|
||||||
|
CertificateInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (keys_and_cert KeysAndCert) Bytes() (bytes []byte) { //, err error) {
|
||||||
|
elg_key := keys_and_cert.PublicKey.(crypto.ElgPublicKey)
|
||||||
|
dsa_key := keys_and_cert.SigningPublicKey.(crypto.DSAPublicKey)
|
||||||
|
bytes = append(bytes, dsa_key[:]...)
|
||||||
|
bytes = append(bytes, elg_key[:]...)
|
||||||
|
bytes = append(bytes, keys_and_cert.CertificateInterface.Cert()...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the PublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
|
// Return the PublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
|
||||||
// determine correct lengths.
|
// determine correct lengths.
|
||||||
//
|
//
|
||||||
func (keys_and_cert KeysAndCert) PublicKey() (key crypto.PublicKey, err error) {
|
func (keys_and_cert KeysAndCert) GetPublicKey() (key crypto.PublicKey, err error) {
|
||||||
cert, err := keys_and_cert.Certificate()
|
cert, err := keys_and_cert.GetCertificate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -74,35 +93,8 @@ func (keys_and_cert KeysAndCert) PublicKey() (key crypto.PublicKey, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cert_len == 0 {
|
if cert_len != 0 {
|
||||||
// No Certificate is present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
key = keys_and_cert.PublicKey
|
||||||
// PublicKey space as ElgPublicKey.
|
|
||||||
var elg_key crypto.ElgPublicKey
|
|
||||||
copy(keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
|
|
||||||
key = elg_key
|
|
||||||
} else {
|
|
||||||
// A Certificate is present in this KeysAndCert
|
|
||||||
cert_type, _ := cert.Type()
|
|
||||||
if cert_type == CERT_KEY {
|
|
||||||
// This KeysAndCert contains a Key Certificate, construct
|
|
||||||
// a PublicKey from the data in the KeysAndCert and
|
|
||||||
// any additional data in the Certificate.
|
|
||||||
key, err = KeyCertificate(cert).ConstructPublicKey(
|
|
||||||
keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Key Certificate is not present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
|
||||||
// PublicKey space as ElgPublicKey. No other Certificate
|
|
||||||
// types are currently in use.
|
|
||||||
var elg_key crypto.ElgPublicKey
|
|
||||||
copy(keys_and_cert[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
|
|
||||||
key = elg_key
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"at": "(KeysAndCert) PublicKey",
|
|
||||||
"cert_type": cert_type,
|
|
||||||
}).Warn("unused certificate type observed")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -111,8 +103,8 @@ func (keys_and_cert KeysAndCert) PublicKey() (key crypto.PublicKey, err error) {
|
|||||||
// Return the SigningPublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
|
// Return the SigningPublicKey for this KeysAndCert, reading from the Key Certificate if it is present to
|
||||||
// determine correct lengths.
|
// determine correct lengths.
|
||||||
//
|
//
|
||||||
func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
func (keys_and_cert KeysAndCert) GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||||
cert, err := keys_and_cert.Certificate()
|
cert, err := keys_and_cert.GetCertificate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -120,31 +112,8 @@ func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.S
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cert_len == 0 {
|
if cert_len != 0 {
|
||||||
// No Certificate is present, return the KEYS_AND_CERT_SPK_SIZE byte
|
signing_public_key = keys_and_cert.SigningPublicKey
|
||||||
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
|
|
||||||
var dsa_pk crypto.DSAPublicKey
|
|
||||||
copy(dsa_pk[:], keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
|
|
||||||
signing_public_key = dsa_pk
|
|
||||||
} else {
|
|
||||||
// A Certificate is present in this KeysAndCert
|
|
||||||
cert_type, _ := cert.Type()
|
|
||||||
if cert_type == CERT_KEY {
|
|
||||||
// This KeysAndCert contains a Key Certificate, construct
|
|
||||||
// a SigningPublicKey from the data in the KeysAndCert and
|
|
||||||
// any additional data in the Certificate.
|
|
||||||
signing_public_key, err = KeyCertificate(cert).ConstructSigningPublicKey(
|
|
||||||
keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Key Certificate is not present, return the KEYS_AND_CERT_SPK_SIZE byte
|
|
||||||
// SigningPublicKey space as legacy SHA DSA1 SigningPublicKey.
|
|
||||||
// No other Certificate types are currently in use.
|
|
||||||
var dsa_pk crypto.DSAPublicKey
|
|
||||||
copy(dsa_pk[:], keys_and_cert[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
|
|
||||||
signing_public_key = dsa_pk
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -153,19 +122,12 @@ func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.S
|
|||||||
// Return the Certificate contained in the KeysAndCert and any errors encountered while parsing the
|
// Return the Certificate contained in the KeysAndCert and any errors encountered while parsing the
|
||||||
// KeysAndCert or Certificate.
|
// KeysAndCert or Certificate.
|
||||||
//
|
//
|
||||||
func (keys_and_cert KeysAndCert) Certificate() (cert Certificate, err error) {
|
func (keys_and_cert KeysAndCert) GetCertificate() (cert CertificateInterface, err error) {
|
||||||
keys_cert_len := len(keys_and_cert)
|
_, err = keys_and_cert.CertificateInterface.Type()
|
||||||
if keys_cert_len < KEYS_AND_CERT_MIN_SIZE {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"at": "(KeysAndCert) Certificate",
|
|
||||||
"data_len": keys_cert_len,
|
|
||||||
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing keys and cert")
|
|
||||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cert, _, err = ReadCertificate(keys_and_cert[KEYS_AND_CERT_DATA_SIZE:])
|
cert = keys_and_cert.CertificateInterface
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,19 +147,93 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
|
|||||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keys_and_cert = KeysAndCert(data[:KEYS_AND_CERT_MIN_SIZE])
|
cert, remainder, err := ReadCertificate(data[:KEYS_AND_CERT_MIN_SIZE])
|
||||||
cert, _ := keys_and_cert.Certificate()
|
if err != nil {
|
||||||
cert_len, cert_len_err := cert.Length()
|
return
|
||||||
|
}
|
||||||
|
spk, pk, remainder, err := ReadKeys(data, cert)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys_and_cert = KeysAndCert{
|
||||||
|
SigningPublicKey: spk,
|
||||||
|
PublicKey: pk,
|
||||||
|
CertificateInterface: cert,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadKeys(data []byte, cert CertificateInterface) (spk crypto.SigningPublicKey, pk crypto.PublicKey, remainder []byte, err error) {
|
||||||
|
data_len := len(data)
|
||||||
|
if data_len < KEYS_AND_CERT_MIN_SIZE {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "ReadKeysAndCert",
|
||||||
|
"data_len": data_len,
|
||||||
|
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||||
|
"reason": "not enough data",
|
||||||
|
}).Error("error parsing keys and cert")
|
||||||
|
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
// No Certificate is present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
||||||
|
// PublicKey space as ElgPublicKey.
|
||||||
|
var elg_key crypto.ElgPublicKey
|
||||||
|
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
|
||||||
|
pk = elg_key
|
||||||
|
} else {
|
||||||
|
// A Certificate is present in this KeysAndCert
|
||||||
|
cert_type, _ := cert.Type()
|
||||||
|
if cert_type == CERT_KEY {
|
||||||
|
// This KeysAndCert contains a Key Certificate, construct
|
||||||
|
// a PublicKey from the data in the KeysAndCert and
|
||||||
|
// any additional data in the Certificate.
|
||||||
|
pk, err = KeyCertificate{PKType: cert_type}.ConstructPublicKey(
|
||||||
|
data[:KEYS_AND_CERT_PUBKEY_SIZE],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Key Certificate is not present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
||||||
|
// PublicKey space as ElgPublicKey. No other Certificate
|
||||||
|
// types are currently in use.
|
||||||
|
var elg_key crypto.ElgPublicKey
|
||||||
|
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], elg_key[:])
|
||||||
|
pk = elg_key
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"at": "(KeysAndCert) PublicKey",
|
||||||
|
"cert_type": cert_type,
|
||||||
|
}).Warn("unused certificate type observed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data_len == 0 {
|
||||||
|
// No Certificate is present, return the KEYS_AND_CERT_SPK_SIZE byte
|
||||||
|
// SigningPublicKey space as legacy DSA SHA1 SigningPublicKey.
|
||||||
|
var dsa_pk crypto.DSAPublicKey
|
||||||
|
copy(dsa_pk[:], data[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
|
||||||
|
spk = dsa_pk
|
||||||
|
} else {
|
||||||
|
// A Certificate is present in this KeysAndCert
|
||||||
|
cert_type, _ := cert.Type()
|
||||||
|
if cert_type == CERT_KEY {
|
||||||
|
// This KeysAndCert contains a Key Certificate, construct
|
||||||
|
// a SigningPublicKey from the data in the KeysAndCert and
|
||||||
|
// any additional data in the Certificate.
|
||||||
|
spk, err = KeyCertificate{SPKType: cert_type}.ConstructSigningPublicKey(
|
||||||
|
data[KEYS_AND_CERT_PUBKEY_SIZE : KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// Key Certificate is not present, return the KEYS_AND_CERT_SPK_SIZE byte
|
||||||
|
// SigningPublicKey space as legacy SHA DSA1 SigningPublicKey.
|
||||||
|
// No other Certificate types are currently in use.
|
||||||
|
var dsa_pk crypto.DSAPublicKey
|
||||||
|
copy(dsa_pk[:], data[KEYS_AND_CERT_PUBKEY_SIZE:KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE])
|
||||||
|
spk = dsa_pk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cert_len, err := cert.Length()
|
||||||
if cert_len == 0 {
|
if cert_len == 0 {
|
||||||
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
|
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if data_len < KEYS_AND_CERT_MIN_SIZE+cert_len {
|
remainder = data[KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE:]
|
||||||
keys_and_cert = append(keys_and_cert, data[KEYS_AND_CERT_MIN_SIZE:]...)
|
|
||||||
err = cert_len_err
|
|
||||||
} else {
|
|
||||||
keys_and_cert = append(keys_and_cert, data[KEYS_AND_CERT_MIN_SIZE:KEYS_AND_CERT_MIN_SIZE+cert_len]...)
|
|
||||||
remainder = data[KEYS_AND_CERT_MIN_SIZE+cert_len:]
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -31,34 +31,56 @@ end_date :: Date
|
|||||||
|
|
||||||
// Sizes or various components of a Lease
|
// Sizes or various components of a Lease
|
||||||
const (
|
const (
|
||||||
LEASE_SIZE = 44
|
LEASE_SIZE = 44
|
||||||
LEASE_HASH_SIZE = 32
|
LEASE_HASH_SIZE = 32
|
||||||
LEASE_TUNNEL_ID_SIZE = 4
|
LEASE_TUNNEL_ID_SIZE = 4
|
||||||
|
LEASE_TUNNEL_DATE_SIZE = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
type Lease [LEASE_SIZE]byte
|
type LeaseInterface interface {
|
||||||
|
TunnelGateway() (hash Hash)
|
||||||
|
TunnelID() uint32
|
||||||
|
Date() (date Date)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Lease struct {
|
||||||
|
LeaseHash Hash
|
||||||
|
TunnelIdent int
|
||||||
|
TunnelDate Date
|
||||||
|
} //[LEASE_SIZE]byte
|
||||||
|
|
||||||
|
var li LeaseInterface = &Lease{}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the first 32 bytes of the Lease as a Hash.
|
// Return the first 32 bytes of the Lease as a Hash.
|
||||||
//
|
//
|
||||||
func (lease Lease) TunnelGateway() (hash Hash) {
|
func (lease Lease) TunnelGateway() (hash Hash) {
|
||||||
copy(hash[:], lease[:LEASE_HASH_SIZE])
|
copy(hash[:], lease.LeaseHash[:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Parse the TunnelID Integer in the Lease.
|
// Return the TunnelID Integer in the Lease.
|
||||||
//
|
//
|
||||||
func (lease Lease) TunnelID() uint32 {
|
func (lease Lease) TunnelID() uint32 {
|
||||||
return uint32(
|
return uint32(lease.TunnelIdent)
|
||||||
Integer(lease[LEASE_HASH_SIZE : LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the Date inside the Lease.
|
// Return the Date inside the Lease.
|
||||||
//
|
//
|
||||||
func (lease Lease) Date() (date Date) {
|
func (lease Lease) Date() (date Date) {
|
||||||
copy(date[:], lease[LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE:])
|
copy(date[:], lease.TunnelDate[:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Possibly temporary? Just to make it compile for now
|
||||||
|
//
|
||||||
|
func (lease Lease) Bytes() (bytes []byte) {
|
||||||
|
var r []byte
|
||||||
|
r = append(r, lease.LeaseHash[:]...)
|
||||||
|
r = append(r, IntegerBytes(lease.TunnelIdent)[:]...)
|
||||||
|
r = append(r, lease.TunnelDate[:]...)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
40
lib/common/lease2.go
Normal file
40
lib/common/lease2.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
Lease2
|
||||||
|
https://geti2p.net/spec/common-structures#lease2
|
||||||
|
Description
|
||||||
|
|
||||||
|
Defines the authorization for a particular tunnel to receive messages targeting a Destination. Same as Lease but with a 4-byte end_date. Used by LeaseSet2. Supported as of 0.9.38; see proposal 123 for more information.
|
||||||
|
Contents
|
||||||
|
|
||||||
|
SHA256 Hash of the RouterIdentity of the gateway router, then the TunnelId, and finally a 4 byte end date.
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| tunnel_gw |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| tunnel_id | end_date |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
||||||
|
tunnel_gw :: Hash of the RouterIdentity of the tunnel gateway
|
||||||
|
length -> 32 bytes
|
||||||
|
|
||||||
|
tunnel_id :: TunnelId
|
||||||
|
length -> 4 bytes
|
||||||
|
|
||||||
|
end_date :: 4 byte date
|
||||||
|
length -> 4 bytes
|
||||||
|
Seconds since the epoch, rolls over in 2106.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
|
||||||
|
Total size: 40 bytes
|
||||||
|
|
||||||
|
JavaDoc: http://echelon.i2p/javadoc/net/i2p/data/Lease2.html
|
||||||
|
*/
|
@ -93,35 +93,52 @@ const (
|
|||||||
LEASE_SET_SIG_SIZE = 40
|
LEASE_SET_SIG_SIZE = 40
|
||||||
)
|
)
|
||||||
|
|
||||||
type LeaseSet []byte
|
type LeaseSetInterface interface {
|
||||||
|
GetPublicKey() (public_key crypto.ElgPublicKey, err error)
|
||||||
|
GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error)
|
||||||
|
/* LeaseCount() (count int, err error)
|
||||||
|
Leases() (leases []Lease, err error)
|
||||||
|
Signature() (signature Signature, err error)
|
||||||
|
Verify() error
|
||||||
|
NewestExpiration() (oldest Date, err error)
|
||||||
|
OldestExpiration() (earliest Date, err error)*/
|
||||||
|
}
|
||||||
|
|
||||||
|
type LeaseSet struct {
|
||||||
|
Destination
|
||||||
|
crypto.SigningPublicKey
|
||||||
|
crypto.ElgPublicKey
|
||||||
|
LeaseList []Lease
|
||||||
|
}
|
||||||
|
|
||||||
|
var lsi LeaseSetInterface = &LeaseSet{}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Read a Destination from the LeaseSet.
|
// Read a Destination from the LeaseSet.
|
||||||
//
|
//
|
||||||
func (lease_set LeaseSet) Destination() (destination Destination, err error) {
|
func (lease_set LeaseSet) GetDestination() (destination Destination, err error) {
|
||||||
keys_and_cert, _, err := ReadKeysAndCert(lease_set)
|
if &lease_set.Destination != nil {
|
||||||
destination = Destination(keys_and_cert)
|
destination = lease_set.Destination
|
||||||
|
} else {
|
||||||
|
err = errors.New("Error leaseset does not contain a destination")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the PublicKey in this LeaseSet and any errors ancountered parsing the LeaseSet.
|
// Return the PublicKey in this LeaseSet and any errors ancountered parsing the LeaseSet.
|
||||||
//
|
//
|
||||||
func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error) {
|
func (lease_set LeaseSet) GetPublicKey() (public_key crypto.ElgPublicKey, err error) {
|
||||||
_, remainder, err := ReadKeysAndCert(lease_set)
|
if lease_set.PublicKey == nil {
|
||||||
remainder_len := len(remainder)
|
|
||||||
if remainder_len < LEASE_SET_PUBKEY_SIZE {
|
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"at": "(LeaseSet) PublicKey",
|
"at": "(LeaseSet) PublicKey",
|
||||||
"data_len": remainder_len,
|
"public": lease_set.PublicKey,
|
||||||
"required_len": LEASE_SET_PUBKEY_SIZE,
|
"reason": "not enough data",
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing public key")
|
}).Error("error parsing public key")
|
||||||
err = errors.New("error parsing public key: not enough data")
|
err = errors.New("error parsing public key: not enough data")
|
||||||
copy(public_key[:], remainder)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
copy(public_key[:], remainder[:LEASE_SET_PUBKEY_SIZE])
|
public_key = lease_set.ElgPublicKey
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,63 +146,24 @@ func (lease_set LeaseSet) PublicKey() (public_key crypto.ElgPublicKey, err error
|
|||||||
// Return the SigningPublicKey, as specified in the LeaseSet's Destination's Key Certificate if
|
// Return the SigningPublicKey, as specified in the LeaseSet's Destination's Key Certificate if
|
||||||
// present, or a legacy DSA key.
|
// present, or a legacy DSA key.
|
||||||
//
|
//
|
||||||
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
func (lease_set LeaseSet) GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||||
destination, err := lease_set.Destination()
|
if lease_set.SigningPublicKey == nil {
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
offset := len(destination) + LEASE_SET_PUBKEY_SIZE
|
|
||||||
cert, err := destination.Certificate()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cert_len, err := cert.Length()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lease_set_len := len(lease_set)
|
|
||||||
if lease_set_len < offset+LEASE_SET_SPK_SIZE {
|
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"at": "(LeaseSet) SigningKey",
|
"at": "(LeaseSet) SigningKey",
|
||||||
"data_len": lease_set_len,
|
"public": lease_set.SigningPublicKey,
|
||||||
"required_len": offset + LEASE_SET_SPK_SIZE,
|
"reason": "not enough data",
|
||||||
"reason": "not enough data",
|
|
||||||
}).Error("error parsing signing public key")
|
}).Error("error parsing signing public key")
|
||||||
err = errors.New("error parsing signing public key: not enough data")
|
err = errors.New("error parsing signing public key: not enough data")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cert_len == 0 {
|
signing_public_key = lease_set.SigningPublicKey
|
||||||
// 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
|
|
||||||
} 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.
|
|
||||||
signing_public_key, err = KeyCertificate(cert).ConstructSigningPublicKey(
|
|
||||||
lease_set[offset : offset+LEASE_SET_SPK_SIZE],
|
|
||||||
)
|
|
||||||
} 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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the number of Leases specified by the LeaseCount value in this LeaseSet.
|
// Return the number of Leases specified by the LeaseCount value in this LeaseSet.
|
||||||
//
|
//
|
||||||
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
/*func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
||||||
_, remainder, err := ReadKeysAndCert(lease_set)
|
_, remainder, err := ReadKeysAndCert(lease_set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -211,12 +189,12 @@ func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
|||||||
err = errors.New("invalid lease set: more than 16 leases")
|
err = errors.New("invalid lease set: more than 16 leases")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// Read the Leases in this LeaseSet, returning a partial set if there is insufficient data.
|
// Read the Leases in this LeaseSet, returning a partial set if there is insufficient data.
|
||||||
//
|
//
|
||||||
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
/*func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
||||||
destination, err := lease_set.Destination()
|
destination, err := lease_set.Destination()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -241,17 +219,17 @@ func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
var lease Lease
|
var lease Lease
|
||||||
copy(lease[:], lease_set[start:end])
|
copy(lease.Bytes(), lease_set[start:end])
|
||||||
leases = append(leases, lease)
|
leases = append(leases, lease)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the Signature data for the LeaseSet, as specified in the Destination's
|
// Return the Signature data for the LeaseSet, as specified in the Destination's
|
||||||
// Key Certificate if present or the 40 bytes following the Leases.
|
// Key Certificate if present or the 40 bytes following the Leases.
|
||||||
//
|
//
|
||||||
func (lease_set LeaseSet) Signature() (signature Signature, err error) {
|
/*func (lease_set LeaseSet) Signature() (signature Signature, err error) {
|
||||||
destination, err := lease_set.Destination()
|
destination, err := lease_set.Destination()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -289,11 +267,12 @@ func (lease_set LeaseSet) Signature() (signature Signature, err error) {
|
|||||||
}
|
}
|
||||||
signature = []byte(lease_set[start:end])
|
signature = []byte(lease_set[start:end])
|
||||||
return
|
return
|
||||||
}
|
}*/
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
/*
|
||||||
func (lease_set LeaseSet) Verify() error {
|
func (lease_set LeaseSet) Verify() error {
|
||||||
//data_end := len(destination) +
|
//data_end := len(destination) +
|
||||||
// LEASE_SET_PUBKEY_SIZE +
|
// LEASE_SET_PUBKEY_SIZE +
|
||||||
@ -345,4 +324,24 @@ func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func ReadLeaseSet(data []byte) (lease_set LeaseSet, remainder []byte, err error) {
|
||||||
|
destination, remainder, err := ReadDestination(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lease_set.Destination = destination
|
||||||
|
spk, pk, remainder, err := ReadKeys(remainder, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lease_set.SigningPublicKey = spk
|
||||||
|
switch pk.(type) {
|
||||||
|
case crypto.ElgPublicKey:
|
||||||
|
lease_set.ElgPublicKey = pk.(crypto.ElgPublicKey)
|
||||||
|
default:
|
||||||
|
err = errors.New("LeaseSet1 uses Elgamal public keys.")
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
105
lib/common/lease_set_2.go
Normal file
105
lib/common/lease_set_2.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
https://geti2p.net/spec/common-structures#leaseset2
|
||||||
|
LeaseSet2
|
||||||
|
Description
|
||||||
|
|
||||||
|
Contained in a I2NP DatabaseStore message of type 3. Supported as of 0.9.38; see proposal 123 for more information.
|
||||||
|
|
||||||
|
Contains all of the currently authorized Lease2 for a particular Destination, and the PublicKey to which garlic messages can be encrypted. A LeaseSet is one of the two structures stored in the network database (the other being RouterInfo), and is keyed under the SHA256 of the contained Destination.
|
||||||
|
Contents
|
||||||
|
|
||||||
|
LeaseSet2Header, followed by a options, then one or more PublicKey for encryption, Integer specifying how many Lease2 structures are in the set, followed by the actual Lease2 structures and finally a Signature of the previous bytes signed by the Destination's SigningPrivateKey or the transient key.
|
||||||
|
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| ls2_header |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| options |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|numk| keytype0| keylen0 | |
|
||||||
|
+----+----+----+----+----+ +
|
||||||
|
| encryption_key_0 |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| keytypen| keylenn | |
|
||||||
|
+----+----+----+----+ +
|
||||||
|
| encryption_key_n |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| num| Lease2 0 |
|
||||||
|
+----+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| Lease2($num-1) |
|
||||||
|
+ +
|
||||||
|
| |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
| signature |
|
||||||
|
~ ~
|
||||||
|
~ ~
|
||||||
|
| |
|
||||||
|
+----+----+----+----+----+----+----+----+
|
||||||
|
|
||||||
|
ls2header :: LeaseSet2Header
|
||||||
|
length -> varies
|
||||||
|
|
||||||
|
options :: Mapping
|
||||||
|
length -> varies, 2 bytes minimum
|
||||||
|
|
||||||
|
numk :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
Number of key types, key lengths, and PublicKeys to follow
|
||||||
|
value: 1 <= numk <= max TBD
|
||||||
|
|
||||||
|
keytype :: The encryption type of the PublicKey to follow.
|
||||||
|
length -> 2 bytes
|
||||||
|
|
||||||
|
keylen :: The length of the PublicKey to follow.
|
||||||
|
Must match the specified length of the encryption type.
|
||||||
|
length -> 2 bytes
|
||||||
|
|
||||||
|
encryption_key :: PublicKey
|
||||||
|
length -> 256 bytes
|
||||||
|
|
||||||
|
num :: Integer
|
||||||
|
length -> 1 byte
|
||||||
|
Number of Lease2s to follow
|
||||||
|
value: 0 <= num <= 16
|
||||||
|
|
||||||
|
leases :: [Lease2]
|
||||||
|
length -> $num*40 bytes
|
||||||
|
|
||||||
|
signature :: Signature
|
||||||
|
length -> 40 bytes or as specified in destination's key
|
||||||
|
certificate, or by the sigtype of the transient public key,
|
||||||
|
if present in the header
|
||||||
|
|
||||||
|
Notes
|
||||||
|
|
||||||
|
The public key of the destination was used for the old I2CP-to-I2CP encryption which was disabled in version 0.6, it is currently unused.
|
||||||
|
The encryption keys are used for end-to-end ElGamal/AES+SessionTag encryption [ELGAMAL-AES] (type 0) or other end-to-end encryption schemes. See [ECIES] and proposals 145 and 156. They may be generated anew at every router startup or they may be persistent. X25519 (type 4, see [ECIES]) is supported as of release 0.9.44.
|
||||||
|
The signature is over the data above, PREPENDED with the single byte containing the DatabaseStore type (3).
|
||||||
|
The signature may be verified using the signing public key of the destination, or the transient signing public key, if an offline signature is included in the leaseset2 header.
|
||||||
|
The key length is provided for each key, so that floodfills and clients may parse the structure even if not all encryption types are known or supported.
|
||||||
|
|
||||||
|
JavaDoc: http://echelon.i2p/javadoc/net/i2p/data/LeaseSet2.html
|
||||||
|
|
||||||
|
*/
|
50
lib/common/length.go
Normal file
50
lib/common/length.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
Length, an extension of Integer
|
||||||
|
https://geti2p.net/spec/common-structures#integer
|
||||||
|
Accurate for version 0.9.24
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
|
||||||
|
// integer and return an int representation.
|
||||||
|
//
|
||||||
|
func Length(number []byte) (value int) {
|
||||||
|
num_len := len(number)
|
||||||
|
if num_len < INTEGER_SIZE {
|
||||||
|
number = append(
|
||||||
|
make([]byte, INTEGER_SIZE-num_len),
|
||||||
|
number...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value = int(binary.BigEndian.Uint64(number))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Take an int representation and return a big endian integer.
|
||||||
|
//
|
||||||
|
func LengthBytes(value int) (number []byte) {
|
||||||
|
onumber := make([]byte, INTEGER_SIZE)
|
||||||
|
// var number []byte
|
||||||
|
binary.BigEndian.PutUint64(onumber, uint64(value))
|
||||||
|
var index int
|
||||||
|
for i, j := range onumber {
|
||||||
|
index = i
|
||||||
|
if j != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(onumber[index:]) == 1 {
|
||||||
|
index--
|
||||||
|
}
|
||||||
|
number = onumber[index:]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -15,22 +15,27 @@ import (
|
|||||||
//
|
//
|
||||||
// A RouterIdentity is identical to KeysAndCert.
|
// A RouterIdentity is identical to KeysAndCert.
|
||||||
//
|
//
|
||||||
type RouterIdentity []byte
|
type RouterIdentity struct {
|
||||||
|
KeysAndCert
|
||||||
|
}
|
||||||
|
|
||||||
func (router_identity RouterIdentity) PublicKey() (crypto.PublicKey, error) {
|
func (router_identity RouterIdentity) PublicKey() (crypto.PublicKey, error) {
|
||||||
return KeysAndCert(router_identity).PublicKey()
|
return router_identity.PublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (router_identity RouterIdentity) SigningPublicKey() (crypto.SigningPublicKey, error) {
|
func (router_identity RouterIdentity) SigningPublicKey() (crypto.SigningPublicKey, error) {
|
||||||
return KeysAndCert(router_identity).SigningPublicKey()
|
return router_identity.SigningPublicKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (router_identity RouterIdentity) Certificate() (Certificate, error) {
|
func (router_identity RouterIdentity) Certificate() (Certificate, error) {
|
||||||
return KeysAndCert(router_identity).Certificate()
|
return router_identity.Certificate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder []byte, err error) {
|
func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder []byte, err error) {
|
||||||
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||||
router_identity = RouterIdentity(keys_and_cert)
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router_identity.KeysAndCert = keys_and_cert
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ func (router_info RouterInfo) IdentHash() (h Hash, err error) {
|
|||||||
var ri RouterIdentity
|
var ri RouterIdentity
|
||||||
ri, err = router_info.RouterIdentity()
|
ri, err = router_info.RouterIdentity()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
h = HashData(ri)
|
h = HashData(ri.Bytes())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -210,7 +210,7 @@ func (router_info RouterInfo) Signature() (signature Signature) {
|
|||||||
head := router_info.optionsLocation()
|
head := router_info.optionsLocation()
|
||||||
size := head + router_info.optionsSize()
|
size := head + router_info.optionsSize()
|
||||||
ident, _ := router_info.RouterIdentity()
|
ident, _ := router_info.RouterIdentity()
|
||||||
keyCert := KeyCertificate(ident)
|
keyCert := ident.CertificateInterface //KeyCertificate(ident)
|
||||||
sigSize := keyCert.SignatureSize()
|
sigSize := keyCert.SignatureSize()
|
||||||
signature = Signature(router_info[size : size+sigSize])
|
signature = Signature(router_info[size : size+sigSize])
|
||||||
return
|
return
|
||||||
@ -224,7 +224,7 @@ func (router_info RouterInfo) optionsLocation() (location int) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
location += len(data)
|
location += len(data.Bytes())
|
||||||
|
|
||||||
remainder_len := len(remainder)
|
remainder_len := len(remainder)
|
||||||
if remainder_len < 9 {
|
if remainder_len < 9 {
|
||||||
|
Reference in New Issue
Block a user