Compare commits
39 Commits
no-more-by
...
structify
Author | SHA1 | Date | |
---|---|---|---|
836f287c9d | |||
d9543745a4 | |||
a87847ef51 | |||
bffc1dfe38 | |||
c181a974cd | |||
27547611ed | |||
3f4d02dc3e | |||
6431100245 | |||
e6b02ab341 | |||
53b629dd15 | |||
a576f0685f | |||
fc0404a11a | |||
cfc3cc97ca | |||
a3b83b5e1e | |||
4a9943de9b | |||
82ba9c1b68 | |||
4f574a28d3 | |||
9d248eda5a | |||
4f19c48da3 | |||
961dfe4266 | |||
afb38b6165 | |||
0aa32b4aad | |||
3adf694c25 | |||
a11c3b73cb | |||
853bc79f8f | |||
77f1c6dd0a | |||
ee7d8a0d63 | |||
c253bf31ac | |||
b97b2854c1 | |||
d5266f8980 | |||
6de4dde1f2 | |||
648c05b15f | |||
bfc7237ba6 | |||
ffbdc7f967 | |||
1cd9d16760 | |||
896df4e483 | |||
49d7eeb441 | |||
7893694c91 | |||
1ea426da9c |
@ -1,26 +0,0 @@
|
||||
# Use the latest 2.1 version of CircleCI pipeline process engine. See: https://circleci.com/docs/2.0/configuration-reference
|
||||
version: 2.1
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/golang:1.15.8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-mod-v4-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: go mod download
|
||||
- save_cache:
|
||||
key: go-mod-v4-{{ checksum "go.sum" }}
|
||||
paths:
|
||||
- "/go/pkg/mod"
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
mkdir -p /tmp/test-reports
|
||||
gotestsum --junitfile /tmp/test-reports/unit-tests.xml
|
||||
- store_test_results:
|
||||
path: /tmp/test-reports
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,4 +5,4 @@
|
||||
*.coverprofile
|
||||
*exportable-fuzz.zip
|
||||
go-i2p
|
||||
*.exe
|
||||
*.exe*.log
|
||||
|
9
.vscode/launch.json
vendored
Normal file
9
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
]
|
||||
}
|
@ -9,6 +9,7 @@ Install required dependencies
|
||||
This example assumes Ubuntu 16.04
|
||||
|
||||
```sh
|
||||
sudo apt-get install pkg-config libsodium-dev
|
||||
go get github.com/hkparker/go-i2p
|
||||
go get github.com/Sirupsen/logrus
|
||||
go get github.com/stretchr/testify/assert
|
||||
|
8
Makefile
8
Makefile
@ -19,7 +19,13 @@ $(EXE):
|
||||
$(GO) build -v -o $(EXE)
|
||||
|
||||
test:
|
||||
$(GO) test -v -failfast ./lib/common
|
||||
$(GO) test -failfast ./...
|
||||
|
||||
clean:
|
||||
$(GO) clean -v
|
||||
|
||||
fmt:
|
||||
find . -name '*.go' -exec gofmt -w -s {} \;
|
||||
|
||||
testcommon:
|
||||
$(GO) test -failfast ./lib/common/...
|
98
README.md
98
README.md
@ -4,17 +4,35 @@ A pure Go implementation of the I2P router.
|
||||
|
||||
## 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
|
||||
|
||||
- Clients
|
||||
- [ ] Datagrams
|
||||
- [ ] I2CP
|
||||
- [ ] Message routing
|
||||
- [ ] SAM
|
||||
- [ ] Streaming
|
||||
- [ ] Tunnel Manager
|
||||
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
|
||||
- Signing
|
||||
- [ ] ECDSA_SHA256_P256
|
||||
@ -30,19 +48,38 @@ go-i2p is in early development.
|
||||
- [ ] RSA_SHA384_3072
|
||||
- [ ] RSA_SHA512_4096
|
||||
- [ ] Ed25519
|
||||
- [ ] Red25519
|
||||
- [ ] ElGamal
|
||||
- [ ] AES256
|
||||
- [ ] X25519
|
||||
- [ ] ChaCha20/Poly1305
|
||||
- [ ] Elligator2
|
||||
- [ ] HKDF
|
||||
- [ ] HMAC
|
||||
- [ ] Noise subsystem
|
||||
- End-to-End Crypto
|
||||
- [ ] Garlic messages
|
||||
- [ ] ElGamal/AES+SessionTag
|
||||
- [ ] Ratchet/X25519
|
||||
- [x] ElGamal
|
||||
- [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
|
||||
- [ ] Message parsing
|
||||
- [ ] Message handling
|
||||
@ -55,26 +92,17 @@ go-i2p is in early development.
|
||||
- [ ] Exploration
|
||||
- [ ] Publishing
|
||||
- [ ] Floodfill
|
||||
- [ ] LS2 and Encrypted Leasesets
|
||||
- Transports
|
||||
- [ ] Transport manager
|
||||
- NTCP
|
||||
- [ ] Handshake
|
||||
- [ ] Session tracking
|
||||
- [ ] Automatic session creation
|
||||
- NTCP2
|
||||
- [ ] Handshake
|
||||
- [ ] Session tracking
|
||||
- [ ] Automatic session creation
|
||||
- SSU
|
||||
- [ ] Handshake
|
||||
- [ ] Session tracking
|
||||
- [ ] Automatic session creation
|
||||
- [ ] Peer Tests
|
||||
- [ ] Introducers
|
||||
- Tunnels
|
||||
- [ ] Building
|
||||
- [ ] Build Message Crypto (ElGamal)
|
||||
- [ ] Build Message Crypto (ECIES)
|
||||
- [ ] Participating
|
||||
- [ ] Tunnel Message Crypto
|
||||
- [ ] Tunnel Message Fragmentation/Reassembly
|
||||
- [ ] SSU
|
||||
|
||||
|
||||
## Contributing
|
||||
|
@ -27,7 +27,7 @@ payload :: data
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -47,42 +47,49 @@ const (
|
||||
CERT_MIN_SIZE = 3
|
||||
)
|
||||
|
||||
type CertificateInterface interface {
|
||||
Cert() []byte
|
||||
Length() (length int, err error)
|
||||
Data() (data []byte, err error)
|
||||
Type() (cert_type int, type_bytes []byte, err error)
|
||||
SignatureSize() (size int)
|
||||
}
|
||||
|
||||
type Certificate struct {
|
||||
kind Integer
|
||||
leng Integer
|
||||
payl []byte
|
||||
CertType *Integer
|
||||
CertLen *Integer
|
||||
CertBytes []byte
|
||||
}
|
||||
|
||||
func (c *Certificate) RawBytes() []byte {
|
||||
bytes := c.kind.Bytes()
|
||||
bytes = append(bytes, c.leng.Bytes()...)
|
||||
bytes = append(bytes, c.payl...)
|
||||
return bytes
|
||||
var ci CertificateInterface = &Certificate{}
|
||||
|
||||
func (certificate Certificate) SignatureSize() (size int) {
|
||||
return 40
|
||||
}
|
||||
|
||||
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
|
||||
func (certificate Certificate) Cert() []byte {
|
||||
var ret []byte
|
||||
ret = append(ret, certificate.CertType.Bytes()...)
|
||||
l, _ := certificate.Length()
|
||||
//if err != nil && err.Error() != "certificate parsing warning: certificate data is shorter than specified by length" {
|
||||
//}
|
||||
data, _ := certificate.Data()
|
||||
if l != 0 && len(data) != 0 {
|
||||
ret = append(ret, certificate.CertLen.Bytes()...)
|
||||
ret = append(ret, data...)
|
||||
} else {
|
||||
ret = append(ret, certificate.CertLen.Bytes()...)
|
||||
}
|
||||
//log.Println("\n\n CERTIFICATE: ", ret, l+CERT_MIN_SIZE, err)
|
||||
return ret //[:l+CERT_MIN_SIZE]
|
||||
}
|
||||
|
||||
//
|
||||
// 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 (c *Certificate) Type() (cert_type int) {
|
||||
cert_type = c.kind.Int()
|
||||
return
|
||||
func (certificate Certificate) Type() (cert_type int, type_bytes []byte, err error) {
|
||||
return certificate.CertType.Value(), certificate.CertType.Bytes(), nil
|
||||
}
|
||||
|
||||
//
|
||||
@ -90,83 +97,62 @@ func (c *Certificate) Type() (cert_type int) {
|
||||
// shorter than the minimum certificate size or if the reported length doesn't
|
||||
// match the provided data.
|
||||
//
|
||||
func (c *Certificate) Length() (length int) {
|
||||
length = c.leng.Int()
|
||||
func (certificate Certificate) Length() (length int, err error) {
|
||||
if certificate.CertLen.Value() < 1 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Certificate) Length",
|
||||
"certificate_bytes_length": certificate.CertLen,
|
||||
"certificate_min_size": CERT_MIN_SIZE - 1,
|
||||
"reason": "certificate is too short",
|
||||
}).Warn("certificate format warning")
|
||||
err = errors.New("error parsing certificate length: certificate is too short")
|
||||
}
|
||||
if certificate.CertLen.Value() > 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")
|
||||
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
||||
length = certificate.CertLen.Value()
|
||||
}
|
||||
if certificate.CertLen.Value() < len(certificate.CertBytes) {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Certificate) Length",
|
||||
"certificate_bytes_length": certificate.CertLen,
|
||||
"certificate_actual_length": len(certificate.CertBytes),
|
||||
"reason": "certificate contains data beyond length",
|
||||
}).Warn("certificate format warning")
|
||||
err = errors.New("certificate parsing warning: certificate data is longer than specified by length")
|
||||
length = certificate.CertLen.Value()
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
length = certificate.CertLen.Value()
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Return the Certificate data and any errors encountered parsing the Certificate.
|
||||
//
|
||||
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:
|
||||
certificate.kind = NewInteger([]byte{0})
|
||||
certificate.leng = NewInteger([]byte{0})
|
||||
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:
|
||||
certificate.kind = NewInteger(data[0:0])
|
||||
certificate.leng = NewInteger([]byte{0})
|
||||
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:
|
||||
certificate.kind = NewInteger(data[0:1])
|
||||
certificate.leng = NewInteger([]byte{0})
|
||||
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:
|
||||
certificate.kind = NewInteger(data[0:1])
|
||||
certificate.leng = NewInteger(data[1:3])
|
||||
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")
|
||||
func (certificate Certificate) Data() (data []byte, err error) {
|
||||
_, err = certificate.Length()
|
||||
data = certificate.CertBytes
|
||||
if err != nil {
|
||||
switch err.Error() {
|
||||
case "error parsing certificate length: certificate is too short":
|
||||
return
|
||||
} 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")
|
||||
case "certificate parsing warning: certificate data is shorter than specified by length":
|
||||
data = certificate.CertBytes
|
||||
return
|
||||
case "certificate parsing warning: certificate data is longer than specified by length":
|
||||
data = certificate.CertBytes[:certificate.CertLen.Value()]
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
@ -174,10 +160,61 @@ func NewCertificate(data []byte) (certificate *Certificate, err error) {
|
||||
// and any errors if a valid Certificate could not be read.
|
||||
//
|
||||
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()
|
||||
err = nil
|
||||
certificate = &Certificate{}
|
||||
certificate.CertType, err = NewInteger(data[0:1])
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Certificate) ReadCertificate",
|
||||
"certificate": certificate,
|
||||
"data": data,
|
||||
"reason": "error parsing certificate type",
|
||||
"error": err,
|
||||
"error_reason": err.Error(),
|
||||
}).Warn("certificate format warning")
|
||||
}
|
||||
certificate.CertLen = &Integer{}
|
||||
cert_len := len(data)
|
||||
|
||||
if cert_len < CERT_MIN_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"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, err = NewInteger(data[1:CERT_MIN_SIZE])
|
||||
// _, err = certificate.Type()
|
||||
//log.Println("Calculated len AT LEN", cert_len, "Stated len AT LEN", certificate.CertLen.Value())
|
||||
if err != nil {
|
||||
//return
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Certificate) ReadCertificate",
|
||||
"certificate_bytes_length": cert_len,
|
||||
"certificate_min_size": CERT_MIN_SIZE,
|
||||
"reason": "certificate size is invalid",
|
||||
}).Warn("certificate format warning")
|
||||
//err = errors.New("error parsing certificate type: certificate type is invalid")
|
||||
}
|
||||
certificate.CertBytes = data[CERT_MIN_SIZE:]
|
||||
_, err = certificate.Length()
|
||||
if err != nil {
|
||||
switch err.Error() {
|
||||
case "error parsing certificate length: certificate is too short":
|
||||
certificate.CertLen, err = NewInteger([]byte{00000000})
|
||||
return
|
||||
case "certificate parsing warning: certificate data is shorter than specified by length":
|
||||
return
|
||||
case "certificate parsing warning: certificate data is longer than specified by length":
|
||||
certificate.CertBytes = data[CERT_MIN_SIZE:]
|
||||
l, _ := certificate.Length()
|
||||
remainder = data[CERT_MIN_SIZE+l:]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -10,8 +10,11 @@ func TestCertificateTypeIsFirstByte(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x00, 0x00}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_type := certificate.Type()
|
||||
certificate, _, err := ReadCertificate(bytes)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
cert_type, _, err := certificate.Type()
|
||||
|
||||
assert.Equal(cert_type, 3, "certificate.Type() should be the first bytes in a certificate")
|
||||
assert.Nil(err)
|
||||
@ -21,19 +24,25 @@ func TestCertificateLengthCorrect(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_len := certificate.Length()
|
||||
certificate, _, err := ReadCertificate(bytes)
|
||||
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||
|
||||
cert_len, err := certificate.Length()
|
||||
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||
|
||||
assert.Equal(cert_len, 2, "certificate.Length() should return integer from second two bytes")
|
||||
assert.Nil(err)
|
||||
assert.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||
}
|
||||
|
||||
func TestCertificateLengthErrWhenTooShort(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x01}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_len := certificate.Length()
|
||||
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()
|
||||
|
||||
assert.Equal(cert_len, 0, "certificate.Length() did not return zero length for missing length data")
|
||||
if assert.NotNil(err) {
|
||||
@ -45,8 +54,15 @@ func TestCertificateLengthErrWhenDataTooShort(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_len := certificate.Length()
|
||||
certificate, _, err := ReadCertificate(bytes)
|
||||
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")
|
||||
}
|
||||
|
||||
cert_len, err := certificate.Length()
|
||||
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(cert_len, 2, "certificate.Length() did not return indicated length when data was actually missing")
|
||||
if assert.NotNil(err) {
|
||||
@ -58,28 +74,34 @@ func TestCertificateDataWhenCorrectSize(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x00, 0x01, 0xaa}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_data := certificate.Data()
|
||||
certificate, _, err := ReadCertificate(bytes)
|
||||
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")
|
||||
cert_len := len(cert_data)
|
||||
|
||||
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, _ := NewInteger(certificate.CertBytes)
|
||||
assert.Equal(170, data.Value(), "certificate.Data() returned incorrect data")
|
||||
}
|
||||
|
||||
func TestCertificateDataWhenTooLong(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x00, 0x02, 0xff, 0xff, 0xaa, 0xaa}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_data := certificate.Data()
|
||||
certificate, _, err := ReadCertificate(bytes)
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
|
||||
cert_len, err := certificate.Length()
|
||||
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is longer than specified by length", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
cert_len := certificate.Length() //len(cert_data)
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
@ -88,8 +110,11 @@ func TestCertificateDataWhenTooShort(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x03, 0x00, 0x02, 0xff}
|
||||
certificate, err := NewCertificate(bytes)
|
||||
cert_data := certificate.Data()
|
||||
certificate, _, err := ReadCertificate(bytes)
|
||||
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")
|
||||
}
|
||||
cert_data, err := certificate.Data()
|
||||
|
||||
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")
|
||||
@ -105,7 +130,8 @@ func TestReadCertificateWithCorrectData(t *testing.T) {
|
||||
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff}
|
||||
cert, remainder, err := ReadCertificate(bytes)
|
||||
|
||||
assert.Equal(cert.length(), 5, "ReadCertificate() did not return correct amount of data for valid certificate")
|
||||
t.Log("CERT IS:", cert.Cert())
|
||||
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.Nil(err, "ReadCertificate() should not return an error with valid data")
|
||||
}
|
||||
@ -116,7 +142,7 @@ func TestReadCertificateWithDataTooShort(t *testing.T) {
|
||||
bytes := []byte{0x00, 0x00, 0x02, 0xff}
|
||||
cert, remainder, err := ReadCertificate(bytes)
|
||||
|
||||
assert.Equal(cert.length(), 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")
|
||||
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")
|
||||
@ -129,10 +155,10 @@ func TestReadCertificateWithRemainder(t *testing.T) {
|
||||
bytes := []byte{0x00, 0x00, 0x02, 0xff, 0xff, 0x01}
|
||||
cert, remainder, err := ReadCertificate(bytes)
|
||||
|
||||
assert.Equal(cert.length(), 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(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
|
||||
assert.Nil(err)
|
||||
assert.Equal(1, int(remainder[0]), "ReadCertificate() did not return correct remainder value")
|
||||
assert.NotNil(err)
|
||||
}
|
||||
|
||||
func TestReadCertificateWithInvalidLength(t *testing.T) {
|
||||
@ -141,7 +167,7 @@ func TestReadCertificateWithInvalidLength(t *testing.T) {
|
||||
bytes := []byte{0x00, 0x00}
|
||||
cert, remainder, err := ReadCertificate(bytes)
|
||||
|
||||
assert.Equal(cert.length(), 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")
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing certificate length: certificate is too short", err.Error(), "correct error message should be returned")
|
||||
|
@ -7,18 +7,39 @@ Accurate for version 0.9.24
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Date [8]byte
|
||||
|
||||
const DATE_SIZE = 8
|
||||
|
||||
//
|
||||
// Time takes the value stored in date as an 8 byte big-endian integer representing the
|
||||
// number of milliseconds since the beginning of unix time and converts it to a Go time.Time
|
||||
// struct.
|
||||
//
|
||||
func (date Date) Time() (date_time time.Time) {
|
||||
seconds := NewInteger(date[:])
|
||||
date_time = time.Unix(0, int64(seconds.Int()*1000000))
|
||||
seconds, _ := NewInteger(date[:])
|
||||
date_time = time.Unix(0, int64(seconds.Value()*1000000))
|
||||
return
|
||||
}
|
||||
|
||||
func ReadDate(data []byte) (h Date, remainder []byte, err error) {
|
||||
if len(data) < DATE_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Date) ReadDate",
|
||||
"data_len": len(data),
|
||||
"required_len": "8",
|
||||
"reason": "date missing data",
|
||||
}).Error("date error")
|
||||
err = errors.New("error reading date, insufficient length")
|
||||
copy(h[:], data[0:len(data)-1])
|
||||
} else {
|
||||
copy(h[:], data[0:DATE_SIZE-1])
|
||||
copy(remainder, data[DATE_SIZE-1:])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -9,42 +9,37 @@ Identical to KeysAndCert
|
||||
*/
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-i2p/go-i2p/lib/common/base32"
|
||||
"github.com/go-i2p/go-i2p/lib/common/base64"
|
||||
"github.com/go-i2p/go-i2p/lib/crypto"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//
|
||||
// A Destination is a KeysAndCert with functionallity
|
||||
// for generating base32 and base64 addresses.
|
||||
//
|
||||
type Destination []byte
|
||||
type Destination struct {
|
||||
KeysAndCert
|
||||
}
|
||||
|
||||
func (destination Destination) PublicKey() (crypto.PublicKey, error) {
|
||||
return KeysAndCert(destination).PublicKey()
|
||||
return destination.KeysAndCert.GetPublicKey()
|
||||
}
|
||||
|
||||
func (destination Destination) SigningPublicKey() (crypto.SigningPublicKey, error) {
|
||||
return KeysAndCert(destination).SigningPublicKey()
|
||||
return destination.KeysAndCert.GetSigningPublicKey()
|
||||
}
|
||||
|
||||
func (destination Destination) Certificate() (*Certificate, error) {
|
||||
return KeysAndCert(destination).Certificate()
|
||||
}
|
||||
|
||||
func ReadDestination(data []byte) (destination Destination, remainder []byte, err error) {
|
||||
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||
destination = Destination(keys_and_cert)
|
||||
return
|
||||
func (destination Destination) Certificate() (CertificateInterface, error) {
|
||||
return destination.KeysAndCert.GetCertificate()
|
||||
}
|
||||
|
||||
//
|
||||
// Generate the I2P base32 address for this Destination.
|
||||
//
|
||||
func (destination Destination) Base32Address() (str string) {
|
||||
hash := crypto.SHA256(destination)
|
||||
hash := crypto.SHA256(destination.Cert())
|
||||
str = strings.Trim(base32.EncodeToString(hash[:]), "=")
|
||||
str = str + ".b32.i2p"
|
||||
return
|
||||
@ -54,5 +49,14 @@ func (destination Destination) Base32Address() (str string) {
|
||||
// Generate the I2P base64 address for this Destination.
|
||||
//
|
||||
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
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
FROM golang
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get upgrade -y
|
||||
apt-get upgrade -y && \
|
||||
apt-get install libsodium-dev -y
|
||||
|
||||
RUN go get github.com/dvyukov/go-fuzz/go-fuzz
|
||||
RUN go get github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
|
@ -2,9 +2,13 @@ package common
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
)
|
||||
|
||||
const HASH_SIZE = 32
|
||||
|
||||
// sha256 hash of some data
|
||||
type Hash [32]byte
|
||||
|
||||
@ -25,3 +29,20 @@ func HashReader(r io.Reader) (h Hash, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReadHash(data []byte) (h Hash, remainder []byte, err error) {
|
||||
if len(data) < HASH_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Hash) ReadHash",
|
||||
"data_len": len(data),
|
||||
"required_len": "32",
|
||||
"reason": "hash missing data",
|
||||
}).Error("hash error")
|
||||
err = errors.New("error reading hash, insufficient length")
|
||||
copy(h[:], data[0:len(data)-1])
|
||||
} else {
|
||||
copy(h[:], data[0:HASH_SIZE-1])
|
||||
copy(remainder, data[HASH_SIZE-1:])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
33
lib/common/ident.go
Normal file
33
lib/common/ident.go
Normal file
@ -0,0 +1,33 @@
|
||||
package common
|
||||
|
||||
/*
|
||||
I2P Tunnel Identity Helpers
|
||||
https://geti2p.net/spec/common-structures#ident
|
||||
Accurate for version 0.9.24
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Ident [4]byte
|
||||
|
||||
const IDENT_SIZE = 4
|
||||
|
||||
func ReadIdent(data []byte) (h Ident, remainder []byte, err error) {
|
||||
if len(data) < IDENT_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Ident) ReadIdent",
|
||||
"data_len": len(data),
|
||||
"required_len": "8",
|
||||
"reason": "ident missing data",
|
||||
}).Error("ident error")
|
||||
err = errors.New("error reading ident, insufficient length")
|
||||
copy(h[:], data[0:len(data)-1])
|
||||
} else {
|
||||
copy(h[:], data[0:IDENT_SIZE-1])
|
||||
copy(remainder, data[IDENT_SIZE-1:])
|
||||
}
|
||||
return
|
||||
}
|
@ -8,6 +8,8 @@ Accurate for version 0.9.24
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
// log "github.com/sirupsen/logrus"
|
||||
// "errors"
|
||||
)
|
||||
|
||||
// Total byte length of an I2P integer
|
||||
@ -17,31 +19,65 @@ const (
|
||||
|
||||
type Integer []byte
|
||||
|
||||
func (i Integer) Bytes() []byte {
|
||||
return i[:]
|
||||
func (i *Integer) longBytes() (value [INTEGER_SIZE]byte) {
|
||||
value = [INTEGER_SIZE]byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
pad := INTEGER_SIZE - len([]byte(*i))
|
||||
for index, element := range []byte(*i) {
|
||||
value[pad+index] = element
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (i Integer) Int() int {
|
||||
return intFromBytes(i.Bytes())
|
||||
func (i *Integer) Value() int {
|
||||
if i == nil {
|
||||
return 0
|
||||
}
|
||||
r := i.longBytes()
|
||||
// log.Println("LONG BYTES", r)
|
||||
return int(binary.BigEndian.Uint64(r[:]))
|
||||
// return int(binary.BigEndian.Int64(r[:]))
|
||||
}
|
||||
|
||||
func NewInteger(bytes []byte) Integer {
|
||||
i := Integer(bytes)
|
||||
return i
|
||||
func (i *Integer) Bytes() []byte {
|
||||
if i == nil {
|
||||
return []byte{}
|
||||
}
|
||||
if len([]byte(*i)) == 0 {
|
||||
return []byte{0}
|
||||
}
|
||||
r := []byte(*i)
|
||||
return r
|
||||
}
|
||||
|
||||
//
|
||||
// Interpret a slice of bytes from length 0 to length 8 as a big-endian
|
||||
// integer and return an int representation.
|
||||
//
|
||||
func intFromBytes(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))
|
||||
func NewInteger(number []byte) (value *Integer, err error) {
|
||||
var integer Integer = number
|
||||
value = &integer //[INTEGER_SIZE]byte(number)
|
||||
// for index, element := range number {
|
||||
// value[INTEGER_SIZE-1-index] = element
|
||||
// }
|
||||
/*length := len(number)
|
||||
if length < INTEGER_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Integer) NewInteger",
|
||||
"length": length,
|
||||
"required_len": INTEGER_SIZE,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing Integer")
|
||||
err = errors.New("error parsing Integer, not enough data")
|
||||
}else if length > INTEGER_SIZE{
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Integer) NewInteger",
|
||||
"length": length,
|
||||
"required_len": INTEGER_SIZE,
|
||||
"reason": "too much data",
|
||||
}).Error("error parsing Integer")
|
||||
err = errors.New("error parsing Integer, too much data")
|
||||
}else{
|
||||
err = nil
|
||||
}*/
|
||||
return
|
||||
}
|
||||
|
@ -1,32 +1,34 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntegerBigEndian(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
|
||||
integer := NewInteger(bytes)
|
||||
integer, err := NewInteger(bytes)
|
||||
assert.Nil(err)
|
||||
|
||||
assert.Equal(integer.Int(), 1, "NewInteger() did not parse bytes big endian")
|
||||
assert.Equal(integer.Value(), 1, "Integer() did not parse bytes big endian")
|
||||
|
||||
checkbytes := integer.Bytes()
|
||||
|
||||
assert.Equal(bytes, checkbytes, "IntegerBytes() did not match original bytes")
|
||||
}
|
||||
|
||||
func TestWorksWithOneByte(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
integer := NewInteger([]byte{0x01})
|
||||
bytes := []byte{0x00}
|
||||
integer, err := NewInteger(bytes)
|
||||
assert.Nil(err)
|
||||
|
||||
assert.Equal(integer.Int(), 1, "NewInteger() did not correctly parse single byte slice")
|
||||
}
|
||||
|
||||
func TestIsZeroWithNoData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
integer := NewInteger([]byte{})
|
||||
|
||||
assert.Equal(integer.Int(), 0, "NewInteger() did not correctly parse zero length byte slice")
|
||||
assert.Equal(integer.Value(), 0, "Integer() did not correctly parse single byte slice")
|
||||
|
||||
checkbytes := integer.Bytes()
|
||||
|
||||
assert.Equal(bytes, checkbytes, "IntegerBytes() did not match original bytes")
|
||||
}
|
||||
|
@ -49,10 +49,10 @@ const (
|
||||
// Key Certificate Public Key Types
|
||||
const (
|
||||
KEYCERT_CRYPTO_ELG = iota
|
||||
)
|
||||
|
||||
const (
|
||||
KEYCERT_MIN_SIZE = 7
|
||||
KEYCERT_CRYPTO_P256
|
||||
KEYCERT_CRYPTO_P384
|
||||
KEYCERT_CRYPTO_P521
|
||||
KEYCERT_CRYPTO_X25519
|
||||
)
|
||||
|
||||
// SigningPublicKey sizes for Signing Key Types
|
||||
@ -79,34 +79,67 @@ const (
|
||||
KEYCERT_SPK_SIZE = 128
|
||||
)
|
||||
|
||||
//type KeyCertificate []byte
|
||||
const (
|
||||
KEYCERT_MIN_SIZE = 7
|
||||
)
|
||||
|
||||
type KeyCertificate struct {
|
||||
*Certificate
|
||||
spkType Integer
|
||||
cpkType Integer
|
||||
}
|
||||
CertificateInterface
|
||||
PKType *Integer
|
||||
PKExtra []byte
|
||||
SPKType *Integer
|
||||
SPKExtra []byte
|
||||
} //[]byte
|
||||
|
||||
//
|
||||
// The data contained in the Key Certificate.
|
||||
//
|
||||
func (key_certificate KeyCertificate) Data() ([]byte, error) {
|
||||
return key_certificate.Certificate.RawBytes(), nil
|
||||
var r []byte
|
||||
r = append(r, key_certificate.CertificateInterface.Cert()...)
|
||||
r = append(r, key_certificate.PKType.Bytes()...)
|
||||
r = append(r, key_certificate.SPKType.Bytes()...)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
//
|
||||
// The SigningPublicKey type this Key Certificate describes and any errors encountered
|
||||
// parsing the KeyCertificate.
|
||||
//
|
||||
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int) {
|
||||
return key_certificate.spkType.Int()
|
||||
func (key_certificate KeyCertificate) SigningPublicKeyType() (signing_pubkey_type int, err error) {
|
||||
// signing_key_type := key_certificate.SPKType
|
||||
// data_len := len(key_certificate.CertificateInterface.CertBytes)
|
||||
if len(key_certificate.SPKType.Bytes()) < 2 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeyCertificate) SigningPublicKeyType",
|
||||
"data_len": len(key_certificate.SPKType.Bytes()),
|
||||
"required_len": 2,
|
||||
"reason": "not enough data",
|
||||
}).Error("error retrieving Signing Public Key type")
|
||||
err = errors.New("error retrieving signing public key type: not enough data")
|
||||
return
|
||||
}
|
||||
log.Println("Signing Public Key Type", key_certificate.SPKType) //.Value())
|
||||
return key_certificate.SPKType.Value(), nil
|
||||
}
|
||||
|
||||
//
|
||||
// The PublicKey type this Key Certificate describes and any errors encountered parsing
|
||||
// this KeyCertificate.
|
||||
//
|
||||
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int) {
|
||||
return key_certificate.cpkType.Int()
|
||||
func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int, err error) {
|
||||
if len(key_certificate.PKType.Bytes()) < 2 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeyCertificate) SingingPublicKeyType",
|
||||
"data_len": len(key_certificate.PKType.Bytes()),
|
||||
"required_len": 2,
|
||||
"reason": "not enough data",
|
||||
}).Error("error retrieving Singning Public Key type")
|
||||
err = errors.New("error retrieving signing public key type: not enough data")
|
||||
return
|
||||
}
|
||||
log.Println("Public Key Type", key_certificate.PKType)
|
||||
return key_certificate.PKType.Value(), nil
|
||||
}
|
||||
|
||||
//
|
||||
@ -114,7 +147,7 @@ func (key_certificate KeyCertificate) PublicKeyType() (pubkey_type int) {
|
||||
// it along with any errors encountered constructing the PublicKey.
|
||||
//
|
||||
func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_key crypto.PublicKey, err error) {
|
||||
key_type := key_certificate.PublicKeyType()
|
||||
key_type, err := key_certificate.SigningPublicKeyType()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -143,7 +176,7 @@ func (key_certificate KeyCertificate) ConstructPublicKey(data []byte) (public_ke
|
||||
// it along with any errors encountered constructing the SigningPublicKey.
|
||||
//
|
||||
func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (signing_public_key crypto.SigningPublicKey, err error) {
|
||||
signing_key_type := key_certificate.PublicKeyType()
|
||||
signing_key_type, err := key_certificate.PublicKeyType()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -175,7 +208,8 @@ func (key_certificate KeyCertificate) ConstructSigningPublicKey(data []byte) (si
|
||||
var ec_key crypto.ECP521PublicKey
|
||||
extra := KEYCERT_SIGN_P521_SIZE - KEYCERT_SPK_SIZE
|
||||
copy(ec_key[:], data)
|
||||
copy(ec_key[KEYCERT_SPK_SIZE:], key_certificate.Certificate.RawBytes()[4:4+extra])
|
||||
d, _ := key_certificate.Data()
|
||||
copy(ec_key[KEYCERT_SPK_SIZE:], d[4:4+extra])
|
||||
signing_public_key = ec_key
|
||||
case KEYCERT_SIGN_RSA2048:
|
||||
//var rsa_key crypto.RSA2048PublicKey
|
||||
@ -207,59 +241,63 @@ func (key_certificate KeyCertificate) SignatureSize() (size int) {
|
||||
KEYCERT_SIGN_ED25519: 64,
|
||||
KEYCERT_SIGN_ED25519PH: 64,
|
||||
}
|
||||
key_type := key_certificate.SigningPublicKeyType()
|
||||
/*if err != nil {
|
||||
key_type, err := key_certificate.SigningPublicKeyType()
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeyCertificate) SignatureSize",
|
||||
"key_type": key_type,
|
||||
"reason": "failed to read signing public key type",
|
||||
}).Error("error getting signature size")
|
||||
return 0
|
||||
}*/
|
||||
}
|
||||
return sizes[int(key_type)]
|
||||
}
|
||||
|
||||
func NewKeyCertificate(bytes []byte) (key_certificate *KeyCertificate, err error) {
|
||||
var certificate *Certificate
|
||||
certificate, _, err = ReadCertificate(bytes)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
if len(bytes) < KEYCERT_MIN_SIZE {
|
||||
err = errors.New("error parsing key certificate: not enough data")
|
||||
//
|
||||
// Read a KeyCertificate from a slice of bytes
|
||||
//
|
||||
func ReadKeyCertificate(data []byte) (key_certificate KeyCertificate, err error) {
|
||||
key_certificate.SPKType = &Integer{}
|
||||
key_certificate.PKType = &Integer{}
|
||||
cert, remainder, err := ReadCertificate(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch len(bytes) {
|
||||
case 4:
|
||||
key_certificate = &KeyCertificate{
|
||||
Certificate: certificate,
|
||||
spkType: Integer(bytes[4:]),
|
||||
cpkType: Integer([]byte{0}),
|
||||
}
|
||||
case 5:
|
||||
key_certificate = &KeyCertificate{
|
||||
Certificate: certificate,
|
||||
spkType: Integer(bytes[4:5]),
|
||||
cpkType: Integer([]byte{0}),
|
||||
}
|
||||
case 6:
|
||||
key_certificate = &KeyCertificate{
|
||||
Certificate: certificate,
|
||||
spkType: Integer(bytes[4:5]),
|
||||
cpkType: Integer(bytes[6:]),
|
||||
}
|
||||
default:
|
||||
key_certificate = &KeyCertificate{
|
||||
Certificate: certificate,
|
||||
spkType: Integer(bytes[4:5]),
|
||||
cpkType: Integer(bytes[6:7]),
|
||||
}
|
||||
cert_type, _, err := cert.Type()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Println("KEYSANDCERT CERT TYPE=", cert_type, cert.CertBytes, remainder)
|
||||
key_certificate.CertificateInterface = cert
|
||||
data = cert.Cert()
|
||||
data_len := len(data)
|
||||
if data_len < KEYCERT_MIN_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeyCertificate) PublicKeyType",
|
||||
"data_len": data_len,
|
||||
"required_len": KEYCERT_MIN_SIZE,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing key certificate public key")
|
||||
err = errors.New("error parsing key certificate public key: not enough data")
|
||||
return
|
||||
}
|
||||
log.Println("KEYSANDCERT=", data, "| len=", data_len, "| 0=", data[0], "| 1=", data[1])
|
||||
key_certificate.SPKType, err = NewInteger(data[len(data)-2 : len(data)])
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeyCertificate) SigningPublicKeyType",
|
||||
"key_type": key_certificate.PKType,
|
||||
"reason": "failed to read signing public key type",
|
||||
}).Error("error parsing key certificate signing public key")
|
||||
}
|
||||
key_certificate.PKType, err = NewInteger(data[len(data)-4 : len(data)-2])
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeyCertificate) PublicKeyType",
|
||||
"key_type": key_certificate.PKType,
|
||||
"reason": "failed to read public key type",
|
||||
}).Error("error parsing key certificate public key")
|
||||
err = errors.New("error parsing key certificate public key: not enough data")
|
||||
}
|
||||
|
||||
//key_certificate.PublicKey = NewPublicKey(bytes)
|
||||
return
|
||||
}
|
||||
|
||||
func KeyCertificateFromCertificate(certificate *Certificate) *KeyCertificate {
|
||||
k, _ := NewKeyCertificate(certificate.RawBytes())
|
||||
return k
|
||||
}
|
||||
|
@ -9,52 +9,60 @@ import (
|
||||
func TestSingingPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
|
||||
pk_type := key_cert.SigningPublicKeyType()
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
|
||||
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||
spk_type, err := key_cert.SigningPublicKeyType()
|
||||
|
||||
assert.Nil(err, "SigningPublicKeyType() returned error with valid data")
|
||||
assert.Equal(pk_type, KEYCERT_SIGN_P521, "SigningPublicKeyType() did not return correct typec")
|
||||
assert.Equal(spk_type, KEYCERT_SIGN_DSA_SHA1, "SigningPublicKeyType() did not return correct type")
|
||||
}
|
||||
|
||||
func TestSingingPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
||||
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
|
||||
sk_type := key_cert.SigningPublicKeyType()
|
||||
|
||||
assert.Equal(sk_type, 0, "SigningPublicKeyType() did not return correct typec")
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x01, 0x00})
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing key certificate public key: not enough data", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
// assert.NotNil(err, "ReadKeyCertificate() returned error with valid data")
|
||||
_, err = key_cert.PublicKeyType()
|
||||
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
|
||||
assert.Equal("error retrieving signing public key type: not enough data", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicKeyTypeReturnsCorrectInteger(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03})
|
||||
pk_type := key_cert.PublicKeyType()
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00})
|
||||
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||
pk_type, err := key_cert.PublicKeyType()
|
||||
|
||||
assert.Nil(err, "PublicKey() returned error with valid data")
|
||||
assert.Equal(pk_type, KEYCERT_SIGN_P521, "PublicKeyType() did not return correct typec")
|
||||
assert.Nil(err, "PublicKeyType() returned error with valid data")
|
||||
assert.Equal(pk_type, KEYCERT_CRYPTO_P521, "PublicKeyType() did not return correct type")
|
||||
}
|
||||
|
||||
func TestPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
||||
func TestSigningPublicKeyTypeReportsWhenDataTooSmall(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
|
||||
pk_type := key_cert.PublicKeyType()
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x02, 0x00, 0x00})
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing key certificate public key: not enough data", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
_, err = key_cert.SigningPublicKeyType()
|
||||
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing key certificate: not enough data", err.Error(), "correct error message should be returned")
|
||||
assert.Equal("error retrieving signing public key type: not enough data", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
assert.Equal(pk_type, 0, "PublicKeyType() did not return correct typec")
|
||||
}
|
||||
|
||||
/*
|
||||
func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||
data := make([]byte, 255)
|
||||
_, err = key_cert.ConstructPublicKey(data)
|
||||
|
||||
@ -62,22 +70,25 @@ func TestConstructPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
||||
assert.Equal("error constructing public key: not enough data", err.Error(), "correct error message should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestConstructPublicKeyReturnsCorrectDataWithElg(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
assert.Nil(err, "ReadKeyCertificate() returned error with valid data")
|
||||
data := make([]byte, 256)
|
||||
pk, err := key_cert.ConstructPublicKey(data)
|
||||
|
||||
assert.Nil(err, "ConstructPublicKey() returned error with valid data")
|
||||
assert.Equal(pk.Len(), 256, "ConstructPublicKey() did not return public key with correct length")
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
data := make([]byte, 127)
|
||||
_, err = key_cert.ConstructSigningPublicKey(data)
|
||||
|
||||
@ -89,7 +100,7 @@ func TestConstructSigningPublicKeyReportsWhenDataTooSmall(t *testing.T) {
|
||||
func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00})
|
||||
data := make([]byte, 128)
|
||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||
|
||||
@ -100,7 +111,7 @@ func TestConstructSigningPublicKeyWithDSASHA1(t *testing.T) {
|
||||
func TestConstructSigningPublicKeyWithP256(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01})
|
||||
data := make([]byte, 128)
|
||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||
|
||||
@ -111,7 +122,7 @@ func TestConstructSigningPublicKeyWithP256(t *testing.T) {
|
||||
func TestConstructSigningPublicKeyWithP384(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02})
|
||||
data := make([]byte, 128)
|
||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||
|
||||
@ -122,10 +133,11 @@ func TestConstructSigningPublicKeyWithP384(t *testing.T) {
|
||||
func TestConstructSigningPublicKeyWithP521(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
key_cert, err := NewKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
|
||||
key_cert, err := ReadKeyCertificate([]byte{0x05, 0x00, 0x08, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00})
|
||||
data := make([]byte, 128)
|
||||
spk, err := key_cert.ConstructSigningPublicKey(data)
|
||||
|
||||
assert.Nil(err, "ConstructSigningPublicKey() with P521 returned err on valid data")
|
||||
assert.Equal(spk.Len(), KEYCERT_SIGN_P521_SIZE, "ConstructSigningPublicKey() with P521 returned incorrect SigningPublicKey length")
|
||||
}
|
||||
*/
|
||||
|
@ -60,51 +60,53 @@ const (
|
||||
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)
|
||||
Bytes() (bytes []byte)
|
||||
}
|
||||
|
||||
type KeysAndCert struct {
|
||||
crypto.SigningPublicKey
|
||||
crypto.PublicKey
|
||||
CertificateInterface
|
||||
}
|
||||
|
||||
func (keys_and_cert KeysAndCert) Bytes() (bytes []byte) { //, err error) {
|
||||
pubkey, _ := keys_and_cert.GetPublicKey()
|
||||
signpubkey, _ := keys_and_cert.GetSigningPublicKey()
|
||||
elg_key := pubkey.(crypto.ElgPublicKey)
|
||||
dsa_key := signpubkey.(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
|
||||
// determine correct lengths.
|
||||
//
|
||||
func (keys_and_cert KeysAndCert) PublicKey() (key crypto.PublicKey, err error) {
|
||||
cert, err := keys_and_cert.Certificate()
|
||||
func (keys_and_cert KeysAndCert) GetPublicKey() (key crypto.PublicKey, err error) {
|
||||
data := make([]byte, KEYS_AND_CERT_PUBKEY_SIZE)
|
||||
if keys_and_cert.PublicKey == nil {
|
||||
epk := crypto.ElgPublicKey{}
|
||||
copy(data[:KEYS_AND_CERT_PUBKEY_SIZE], epk[:])
|
||||
keys_and_cert.PublicKey = epk
|
||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||
}
|
||||
/*cert, err := keys_and_cert.GetCertificate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cert_len := cert.Length()
|
||||
cert_len, err := cert.Length()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cert_len == 0 {
|
||||
// No Certificate is present, return the KEYS_AND_CERT_PUBKEY_SIZE byte
|
||||
// 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 = KeyCertificateFromCertificate(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")
|
||||
}
|
||||
|
||||
}
|
||||
if cert_len != 0 {*/
|
||||
key = keys_and_cert.PublicKey
|
||||
/*}*/
|
||||
return
|
||||
}
|
||||
|
||||
@ -112,41 +114,22 @@ 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
|
||||
// determine correct lengths.
|
||||
//
|
||||
func (keys_and_cert KeysAndCert) SigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||
cert, err := keys_and_cert.Certificate()
|
||||
func (keys_and_cert KeysAndCert) GetSigningPublicKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||
if keys_and_cert.SigningPublicKey == nil {
|
||||
keys_and_cert.SigningPublicKey = crypto.DSAPublicKey{}
|
||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||
}
|
||||
/*cert, err := keys_and_cert.GetCertificate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cert_len := cert.Length()
|
||||
cert_len, err := cert.Length()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cert_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[:], 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 = KeyCertificateFromCertificate(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
|
||||
}
|
||||
|
||||
}
|
||||
if cert_len != 0 {*/
|
||||
signing_public_key = keys_and_cert.SigningPublicKey
|
||||
/*}*/
|
||||
return
|
||||
}
|
||||
|
||||
@ -154,20 +137,111 @@ 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
|
||||
// KeysAndCert or Certificate.
|
||||
//
|
||||
func (keys_and_cert KeysAndCert) Certificate() (cert *Certificate, err error) {
|
||||
keys_cert_len := len(keys_and_cert)
|
||||
if keys_cert_len < KEYS_AND_CERT_MIN_SIZE {
|
||||
func (keys_and_cert KeysAndCert) GetCertificate() (cert CertificateInterface, err error) {
|
||||
data_len := len(keys_and_cert.Bytes())
|
||||
log.Println("LEN IS", data_len, "KEYS_AND_CERT_MIN_SIZE", KEYS_AND_CERT_MIN_SIZE)
|
||||
if data_len < KEYS_AND_CERT_MIN_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(KeysAndCert) Certificate",
|
||||
"data_len": keys_cert_len,
|
||||
"at": "GetCertificate",
|
||||
"data_len": data_len,
|
||||
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing keys and cert")
|
||||
err = errors.New("certificate parsing warning: certificate data is shorter than specified by length")
|
||||
}
|
||||
/*if data_len > CERT_MIN_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "ReadKeysAndCert",
|
||||
"data_len": data_len,
|
||||
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||
"reason": "too much data",
|
||||
}).Error("error parsing keys and cert")
|
||||
err = errors.New("certificate parsing warning: certificate data is longer than specified by length")
|
||||
}*/
|
||||
cert = keys_and_cert.CertificateInterface
|
||||
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": "ReadKeys",
|
||||
"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
|
||||
}
|
||||
cert, _, err = ReadCertificate(keys_and_cert[KEYS_AND_CERT_DATA_SIZE:])
|
||||
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_bytes, e := cert.Type()
|
||||
err = e
|
||||
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.
|
||||
cert_integer, _ := NewInteger(cert_bytes)
|
||||
pk, err = KeyCertificate{PKType: cert_integer}.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_bytes, e := cert.Type()
|
||||
err = e
|
||||
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.
|
||||
cert_integer, _ := NewInteger(cert_bytes)
|
||||
spk, err = KeyCertificate{SPKType: cert_integer}.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, e := cert.Length()
|
||||
err = e
|
||||
if cert_len == 0 {
|
||||
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
|
||||
return
|
||||
}
|
||||
remainder = data[KEYS_AND_CERT_PUBKEY_SIZE+KEYS_AND_CERT_SPK_SIZE:]
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
@ -176,6 +250,7 @@ func (keys_and_cert KeysAndCert) Certificate() (cert *Certificate, err error) {
|
||||
//
|
||||
func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte, err error) {
|
||||
data_len := len(data)
|
||||
keys_and_cert.CertificateInterface = &Certificate{}
|
||||
if data_len < KEYS_AND_CERT_MIN_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "ReadKeysAndCert",
|
||||
@ -186,19 +261,20 @@ func ReadKeysAndCert(data []byte) (keys_and_cert KeysAndCert, remainder []byte,
|
||||
err = errors.New("error parsing KeysAndCert: data is smaller than minimum valid size")
|
||||
return
|
||||
}
|
||||
keys_and_cert = KeysAndCert(data[:KEYS_AND_CERT_MIN_SIZE])
|
||||
cert, _ := keys_and_cert.Certificate()
|
||||
cert_len := cert.Length()
|
||||
if cert_len == 0 {
|
||||
remainder = data[KEYS_AND_CERT_MIN_SIZE:]
|
||||
cert, remainder, err := ReadCertificate(data[KEYS_AND_CERT_DATA_SIZE:])
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "ReadKeysAndCert",
|
||||
"data_len": data_len,
|
||||
"required_len": KEYS_AND_CERT_MIN_SIZE,
|
||||
"reason": "error parsing certificate",
|
||||
}).Error("error parsing keys and cert")
|
||||
err = errors.New("error parsing KeysAndCert: error parsing certificate")
|
||||
return
|
||||
}
|
||||
if data_len < KEYS_AND_CERT_MIN_SIZE+cert_len {
|
||||
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:]
|
||||
}
|
||||
keys_and_cert.CertificateInterface = cert
|
||||
spk, pk, remainder, err := ReadKeys(data, cert)
|
||||
keys_and_cert.SigningPublicKey = spk
|
||||
keys_and_cert.PublicKey = pk
|
||||
return
|
||||
}
|
||||
|
@ -9,19 +9,25 @@ import (
|
||||
func TestCertificateWithMissingData(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
|
||||
//cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01}
|
||||
data := make([]byte, 128+256)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
|
||||
cert, err := keys_and_cert.Certificate()
|
||||
//data = append(data, cert_data...)
|
||||
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
t.Log("\n\nREMAINDER", remainder, "\n\n")
|
||||
cert, err := keys_and_cert.GetCertificate()
|
||||
t.Log("\n\nSTART\n\n")
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
} else {
|
||||
t.Log("\n\nEND\n\n", cert.Cert())
|
||||
}
|
||||
cert_bytes := cert.Bytes()
|
||||
if assert.Equal(len(cert_data), len(cert_bytes)) {
|
||||
assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return available data when cert was missing some data")
|
||||
}
|
||||
// cert_bytes := []byte(cert.Cert())
|
||||
// if assert.Equal(len(cert_data), len(cert_bytes)) {
|
||||
// assert.Equal(cert_bytes, cert_data, "keys_and_cert.GetCertificate() did not return available data when cert was missing some data")
|
||||
// }
|
||||
}
|
||||
|
||||
func TestCertificateWithValidData(t *testing.T) {
|
||||
@ -30,13 +36,13 @@ func TestCertificateWithValidData(t *testing.T) {
|
||||
cert_data := []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}
|
||||
data := make([]byte, 128+256)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
cert, err := keys_and_cert.Certificate()
|
||||
cert, err := keys_and_cert.GetCertificate()
|
||||
assert.Nil(err)
|
||||
cert_bytes := cert.Bytes()
|
||||
cert_bytes := []byte(cert.Cert())
|
||||
if assert.Equal(len(cert_data), len(cert_bytes)) {
|
||||
assert.Equal(cert_bytes, cert_data, "keys_and_cert.Certificate() did not return correct data with valid cert")
|
||||
assert.Equal(cert_bytes, cert_data, "keys_and_cert.GetCertificate() did not return correct data with valid cert")
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,13 +54,15 @@ func TestPublicKeyWithBadData(t *testing.T) {
|
||||
data := make([]byte, 128)
|
||||
data = append(data, pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
pub_key, err := keys_and_cert.PublicKey()
|
||||
//pub_key
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
assert.Nil(pub_key)
|
||||
//TODO: pub_key in this instance is a null key(all zeros). This test should be changed to check for this.
|
||||
//assert.Nil(pub_key)
|
||||
}
|
||||
|
||||
func TestPublicKeyWithBadCertificate(t *testing.T) {
|
||||
@ -65,13 +73,15 @@ func TestPublicKeyWithBadCertificate(t *testing.T) {
|
||||
data := make([]byte, 128)
|
||||
data = append(data, pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
pub_key, err := keys_and_cert.PublicKey()
|
||||
//pub_key
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
assert.Nil(pub_key)
|
||||
//TODO: pub_key in this instance is a null key(all zeros). This test should be changed to check for this.
|
||||
//assert.Nil(pub_key)
|
||||
}
|
||||
|
||||
func TestPublicKeyWithNullCertificate(t *testing.T) {
|
||||
@ -82,9 +92,9 @@ func TestPublicKeyWithNullCertificate(t *testing.T) {
|
||||
data := make([]byte, 128)
|
||||
data = append(data, pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
pub_key, err := keys_and_cert.PublicKey()
|
||||
pub_key, err := keys_and_cert.GetPublicKey()
|
||||
assert.Nil(err)
|
||||
assert.Equal(len(pub_key_data), pub_key.Len())
|
||||
}
|
||||
@ -97,9 +107,10 @@ func TestPublicKeyWithKeyCertificate(t *testing.T) {
|
||||
data := make([]byte, 128)
|
||||
data = append(data, pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
assert.Nil(err)
|
||||
|
||||
pub_key, err := keys_and_cert.PublicKey()
|
||||
pub_key, err := keys_and_cert.GetPublicKey()
|
||||
assert.Nil(err)
|
||||
assert.Equal(len(pub_key_data), pub_key.Len())
|
||||
}
|
||||
@ -112,9 +123,9 @@ func TestSigningPublicKeyWithBadData(t *testing.T) {
|
||||
data := make([]byte, 93)
|
||||
data = append(data, pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
signing_pub_key, err := keys_and_cert.SigningPublicKey()
|
||||
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
@ -129,9 +140,9 @@ func TestSigningPublicKeyWithBadCertificate(t *testing.T) {
|
||||
data := make([]byte, 128)
|
||||
data = append(data, pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
signing_pub_key, err := keys_and_cert.SigningPublicKey()
|
||||
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
}
|
||||
@ -146,9 +157,9 @@ func TestSigningPublicKeyWithNullCertificate(t *testing.T) {
|
||||
signing_pub_key_data := make([]byte, 128)
|
||||
data := append(pub_key_data, signing_pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
signing_pub_key, err := keys_and_cert.SigningPublicKey()
|
||||
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||
assert.Nil(err)
|
||||
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
|
||||
}
|
||||
@ -161,9 +172,9 @@ func TestSigningPublicKeyWithKeyCertificate(t *testing.T) {
|
||||
signing_pub_key_data := make([]byte, 128)
|
||||
data := append(pub_key_data, signing_pub_key_data...)
|
||||
data = append(data, cert_data...)
|
||||
keys_and_cert := KeysAndCert(data)
|
||||
keys_and_cert, _, err := ReadKeysAndCert(data)
|
||||
|
||||
signing_pub_key, err := keys_and_cert.SigningPublicKey()
|
||||
signing_pub_key, err := keys_and_cert.GetSigningPublicKey()
|
||||
assert.Nil(err)
|
||||
assert.Equal(len(signing_pub_key_data), signing_pub_key.Len())
|
||||
}
|
||||
@ -178,15 +189,15 @@ func TestReadKeysAndCertWithMissingData(t *testing.T) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
|
||||
_, err = keys_and_cert.PublicKey()
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
_, err = keys_and_cert.SigningPublicKey()
|
||||
_, err = keys_and_cert.GetSigningPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
_, err = keys_and_cert.Certificate()
|
||||
_, err = keys_and_cert.GetCertificate()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error())
|
||||
}
|
||||
@ -203,15 +214,15 @@ func TestReadKeysAndCertWithMissingCertData(t *testing.T) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
}
|
||||
|
||||
_, err = keys_and_cert.PublicKey()
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
}
|
||||
_, err = keys_and_cert.SigningPublicKey()
|
||||
_, err = keys_and_cert.GetSigningPublicKey()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
}
|
||||
_, err = keys_and_cert.Certificate()
|
||||
_, err = keys_and_cert.GetCertificate()
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal("certificate parsing warning: certificate data is shorter than specified by length", err.Error())
|
||||
}
|
||||
@ -226,12 +237,12 @@ func TestReadKeysAndCertWithValidDataWithCertificate(t *testing.T) {
|
||||
assert.Equal(0, len(remainder))
|
||||
assert.Nil(err)
|
||||
|
||||
_, err = keys_and_cert.PublicKey()
|
||||
assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.SigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.Certificate()
|
||||
assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.GetSigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.GetCertificate()
|
||||
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data containing certificate")
|
||||
}
|
||||
|
||||
func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
|
||||
@ -243,12 +254,12 @@ func TestReadKeysAndCertWithValidDataWithoutCertificate(t *testing.T) {
|
||||
assert.Equal(0, len(remainder))
|
||||
assert.Nil(err)
|
||||
|
||||
_, err = keys_and_cert.PublicKey()
|
||||
assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.SigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.Certificate()
|
||||
assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.GetSigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.GetCertificate()
|
||||
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data not containing certificate")
|
||||
}
|
||||
|
||||
func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
|
||||
@ -262,12 +273,12 @@ func TestReadKeysAndCertWithValidDataWithCertificateAndRemainder(t *testing.T) {
|
||||
}
|
||||
assert.Nil(err)
|
||||
|
||||
_, err = keys_and_cert.PublicKey()
|
||||
assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.SigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.Certificate()
|
||||
assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.GetSigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data containing certificate")
|
||||
_, err = keys_and_cert.GetCertificate()
|
||||
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data containing certificate")
|
||||
}
|
||||
|
||||
func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T) {
|
||||
@ -281,10 +292,10 @@ func TestReadKeysAndCertWithValidDataWithoutCertificateAndRemainder(t *testing.T
|
||||
}
|
||||
assert.Nil(err)
|
||||
|
||||
_, err = keys_and_cert.PublicKey()
|
||||
assert.Nil(err, "keys_and_cert.PublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.SigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.SigningPublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.Certificate()
|
||||
assert.Nil(err, "keys_and_cert.Certificate() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.GetPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetPublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.GetSigningPublicKey()
|
||||
assert.Nil(err, "keys_and_cert.GetSigningPublicKey() returned error with valid data not containing certificate")
|
||||
_, err = keys_and_cert.GetCertificate()
|
||||
assert.Nil(err, "keys_and_cert.GetCertificate() returned error with valid data not containing certificate")
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
/*
|
||||
I2P Lease
|
||||
https://geti2p.net/spec/common-structures#lease
|
||||
@ -31,35 +36,73 @@ end_date :: Date
|
||||
|
||||
// Sizes or various components of a Lease
|
||||
const (
|
||||
LEASE_SIZE = 44
|
||||
LEASE_HASH_SIZE = 32
|
||||
LEASE_TUNNEL_ID_SIZE = 4
|
||||
LEASE_SIZE = 44
|
||||
LEASE_HASH_SIZE = 32
|
||||
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 *Integer
|
||||
TunnelDate Date
|
||||
} //[LEASE_SIZE]byte
|
||||
|
||||
var li LeaseInterface = &Lease{}
|
||||
|
||||
//
|
||||
// Return the first 32 bytes of the Lease as a Hash.
|
||||
//
|
||||
func (lease Lease) TunnelGateway() (hash Hash) {
|
||||
copy(hash[:], lease[:LEASE_HASH_SIZE])
|
||||
copy(hash[:], lease.LeaseHash[:])
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Parse the TunnelID Integer in the Lease.
|
||||
// Return the TunnelID Integer in the Lease.
|
||||
//
|
||||
func (lease Lease) TunnelID() uint32 {
|
||||
i := NewInteger(lease[LEASE_HASH_SIZE : LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE])
|
||||
return uint32(
|
||||
i.Int(),
|
||||
)
|
||||
return uint32(lease.TunnelIdent.Value())
|
||||
}
|
||||
|
||||
//
|
||||
// Return the Date inside the Lease.
|
||||
//
|
||||
func (lease Lease) Date() (date Date) {
|
||||
copy(date[:], lease[LEASE_HASH_SIZE+LEASE_TUNNEL_ID_SIZE:])
|
||||
copy(date[:], lease.TunnelDate[:])
|
||||
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, lease.TunnelIdent.Bytes()...)
|
||||
r = append(r, lease.TunnelDate[:]...)
|
||||
return r
|
||||
}
|
||||
|
||||
func ReadLease(data []byte) (lease Lease, remainder []byte, err error) {
|
||||
if len(data) < LEASE_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(Lease) ReadLease",
|
||||
"data_len": len(data),
|
||||
"required_len": "44",
|
||||
"reason": "lease missing data",
|
||||
}).Error("error parsnig lease")
|
||||
err = errors.New("error parsing lease: lease missing data")
|
||||
}
|
||||
lease.LeaseHash, remainder, err = ReadHash(data)
|
||||
identbytes, remainder, err := ReadIdent(remainder)
|
||||
lease.TunnelIdent, err = NewInteger(identbytes[:])
|
||||
lease.TunnelDate, remainder, err = ReadDate(remainder)
|
||||
return
|
||||
}
|
||||
|
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
|
||||
*/
|
@ -82,7 +82,6 @@ signature :: Signature
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-i2p/go-i2p/lib/crypto"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -94,35 +93,44 @@ const (
|
||||
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)
|
||||
Leases() (leases []Lease, err error)
|
||||
/* LeaseCount() (count int, err error)*/
|
||||
|
||||
GetSignature() (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.
|
||||
//
|
||||
func (lease_set LeaseSet) Destination() (destination Destination, err error) {
|
||||
keys_and_cert, _, err := ReadKeysAndCert(lease_set)
|
||||
destination = Destination(keys_and_cert)
|
||||
func (lease_set LeaseSet) GetDestination() (destination Destination, err error) {
|
||||
if &lease_set.Destination != nil {
|
||||
destination = lease_set.Destination
|
||||
} else {
|
||||
err = errors.New("Error leaseset does not contain a destination")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Return the PublicKey in this LeaseSet and any errors ancountered parsing the LeaseSet.
|
||||
//
|
||||
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(log.Fields{
|
||||
"at": "(LeaseSet) PublicKey",
|
||||
"data_len": remainder_len,
|
||||
"required_len": LEASE_SET_PUBKEY_SIZE,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing public key")
|
||||
err = errors.New("error parsing public key: not enough data")
|
||||
copy(public_key[:], remainder)
|
||||
return
|
||||
}
|
||||
copy(public_key[:], remainder[:LEASE_SET_PUBKEY_SIZE])
|
||||
func (lease_set LeaseSet) GetPublicKey() (public_key crypto.ElgPublicKey, err error) {
|
||||
public_key = lease_set.ElgPublicKey
|
||||
return
|
||||
}
|
||||
|
||||
@ -130,56 +138,22 @@ 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
|
||||
// present, or a legacy DSA key.
|
||||
//
|
||||
func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||
destination, err := lease_set.Destination()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
offset := len(destination) + LEASE_SET_PUBKEY_SIZE
|
||||
cert, err := destination.Certificate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cert_len := cert.Length()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lease_set_len := len(lease_set)
|
||||
if lease_set_len < offset+LEASE_SET_SPK_SIZE {
|
||||
func (lease_set LeaseSet) GetSigningKey() (signing_public_key crypto.SigningPublicKey, err error) {
|
||||
if lease_set.SigningPublicKey == nil {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(LeaseSet) SigningKey",
|
||||
"data_len": lease_set_len,
|
||||
"required_len": offset + LEASE_SET_SPK_SIZE,
|
||||
"reason": "not enough data",
|
||||
"at": "(LeaseSet) SigningKey",
|
||||
"public": lease_set.SigningPublicKey,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing signing public key")
|
||||
err = errors.New("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
|
||||
} 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 = KeyCertificateFromCertificate(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
|
||||
}
|
||||
signing_public_key = lease_set.SigningPublicKey
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
||||
leases = lease_set.LeaseList
|
||||
return
|
||||
}
|
||||
|
||||
@ -187,65 +161,7 @@ func (lease_set LeaseSet) SigningKey() (signing_public_key crypto.SigningPublicK
|
||||
// Return the number of Leases specified by the LeaseCount value in this LeaseSet.
|
||||
//
|
||||
func (lease_set LeaseSet) LeaseCount() (count int, err error) {
|
||||
_, remainder, err := ReadKeysAndCert(lease_set)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
remainder_len := len(remainder)
|
||||
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
|
||||
log.WithFields(log.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 = errors.New("error parsing lease count: not enough data")
|
||||
return
|
||||
}
|
||||
c := NewInteger([]byte{remainder[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
|
||||
count = c.Int()
|
||||
if count > 16 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(LeaseSet) LeaseCount",
|
||||
"lease_count": count,
|
||||
"reason": "more than 16 leases",
|
||||
}).Warn("invalid lease set")
|
||||
err = errors.New("invalid lease set: more than 16 leases")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Read the Leases in this LeaseSet, returning a partial set if there is insufficient data.
|
||||
//
|
||||
func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
||||
destination, err := lease_set.Destination()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
offset := len(destination) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
|
||||
count, err := lease_set.LeaseCount()
|
||||
if err != nil {
|
||||
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(log.Fields{
|
||||
"at": "(LeaseSet) Leases",
|
||||
"data_len": lease_set_len,
|
||||
"required_len": end,
|
||||
"reason": "some leases missing",
|
||||
}).Error("error parsnig lease set")
|
||||
err = errors.New("error parsing lease set: some leases missing")
|
||||
return
|
||||
}
|
||||
var lease Lease
|
||||
copy(lease[:], lease_set[start:end])
|
||||
leases = append(leases, lease)
|
||||
}
|
||||
count = len(lease_set.LeaseList)
|
||||
return
|
||||
}
|
||||
|
||||
@ -253,49 +169,14 @@ func (lease_set LeaseSet) Leases() (leases []Lease, err error) {
|
||||
// Return the Signature data for the LeaseSet, as specified in the Destination's
|
||||
// Key Certificate if present or the 40 bytes following the Leases.
|
||||
//
|
||||
func (lease_set LeaseSet) Signature() (signature Signature, err error) {
|
||||
destination, err := lease_set.Destination()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lease_count, err := lease_set.LeaseCount()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
start := len(destination) +
|
||||
LEASE_SET_PUBKEY_SIZE +
|
||||
LEASE_SET_SPK_SIZE +
|
||||
1 +
|
||||
(LEASE_SIZE * lease_count)
|
||||
cert, err := destination.Certificate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cert_type := cert.Type()
|
||||
var end int
|
||||
if cert_type == CERT_KEY {
|
||||
end = start + KeyCertificateFromCertificate(cert).SignatureSize()
|
||||
} else {
|
||||
end = start + LEASE_SET_SIG_SIZE
|
||||
}
|
||||
lease_set_len := len(lease_set)
|
||||
if lease_set_len < end {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(LeaseSet) Signature",
|
||||
"data_len": lease_set_len,
|
||||
"required_len": end,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing signatre")
|
||||
err = errors.New("error parsing signature: not enough data")
|
||||
return
|
||||
}
|
||||
signature = []byte(lease_set[start:end])
|
||||
func (lease_set LeaseSet) GetSignature() (signature Signature, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
/*
|
||||
func (lease_set LeaseSet) Verify() error {
|
||||
//data_end := len(destination) +
|
||||
// LEASE_SET_PUBKEY_SIZE +
|
||||
@ -312,7 +193,7 @@ func (lease_set LeaseSet) Verify() error {
|
||||
//}
|
||||
return nil // verifier.Verify(data, lease_set.Signature())
|
||||
}
|
||||
|
||||
*/
|
||||
//
|
||||
// Return the oldest date from all the Leases in the LeaseSet.
|
||||
//
|
||||
@ -348,3 +229,108 @@ func (lease_set LeaseSet) OldestExpiration() (earliest Date, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReadLeaseSetSignature(bytes []byte, cert CertificateInterface) (signature Signature, remainder []byte, err error) {
|
||||
start := 0
|
||||
cert_type, _, _ := cert.Type()
|
||||
var end int
|
||||
if cert_type == CERT_KEY {
|
||||
end = start + cert.SignatureSize()
|
||||
} else {
|
||||
end = start + LEASE_SET_SIG_SIZE
|
||||
}
|
||||
bytes_len := len(bytes)
|
||||
if bytes_len < end {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(LeaseSet) Signature",
|
||||
"data_len": bytes_len,
|
||||
"required_len": end,
|
||||
"reason": "not enough data",
|
||||
}).Error("error parsing signatre")
|
||||
err = errors.New("error parsing signature: not enough data")
|
||||
signature = []byte(bytes[start:bytes_len])
|
||||
return
|
||||
}
|
||||
signature = []byte(bytes[start:end])
|
||||
return
|
||||
}
|
||||
|
||||
func ReadLeaseCount(bytes []byte) (count *Integer, err error) {
|
||||
remainder_len := len(bytes)
|
||||
if remainder_len < LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE+1 {
|
||||
log.WithFields(log.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 = errors.New("error parsing lease count: not enough data")
|
||||
return
|
||||
}
|
||||
count, err = NewInteger([]byte{bytes[LEASE_SET_PUBKEY_SIZE+LEASE_SET_SPK_SIZE]})
|
||||
if count.Value() > 16 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(LeaseSet) LeaseCount",
|
||||
"lease_count": count,
|
||||
"reason": "more than 16 leases",
|
||||
}).Warn("invalid lease set")
|
||||
err = errors.New("invalid lease set: more than 16 leases")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Read the Leases in this LeaseSet, returning a partial set if there is insufficient data.
|
||||
//
|
||||
func ReadLeases(bytes []byte) (leases []Lease, remainder []byte, err error) {
|
||||
count, err := ReadLeaseCount(bytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < count.Value(); i++ {
|
||||
start := 0 //offset + (i * LEASE_SIZE)
|
||||
end := start + LEASE_SIZE
|
||||
lease_set_len := len(bytes)
|
||||
if lease_set_len < end {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(LeaseSet) Leases",
|
||||
"data_len": lease_set_len,
|
||||
"required_len": end,
|
||||
"reason": "some leases missing",
|
||||
}).Error("error parsnig lease set")
|
||||
err = errors.New("error parsing lease set: some leases missing")
|
||||
return
|
||||
}
|
||||
var lease Lease
|
||||
lease, remainder, err = ReadLease(bytes[start:end])
|
||||
leases = append(leases, lease)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReadLeaseSetKeys(data []byte, cert CertificateInterface) (spk crypto.SigningPublicKey, pk crypto.ElgPublicKey, remainder []byte, err error) {
|
||||
spk, ppk, remainder, err := ReadKeys(data, cert)
|
||||
switch ppk.(type) {
|
||||
case crypto.ElgPublicKey:
|
||||
pk = ppk.(crypto.ElgPublicKey)
|
||||
default:
|
||||
err = errors.New("LeaseSet1 uses Elgamal public keys.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ReadLeaseSet(data []byte) (lease_set LeaseSet, remainder []byte, err error) {
|
||||
destination, remainder, err := ReadDestination(data)
|
||||
lease_set.Destination = destination
|
||||
//offset := len(destination.Bytes()) + LEASE_SET_PUBKEY_SIZE + LEASE_SET_SPK_SIZE + 1
|
||||
spk, pk, remainder, err := ReadLeaseSetKeys(remainder, nil)
|
||||
lease_set.SigningPublicKey = spk
|
||||
lease_set.ElgPublicKey = pk
|
||||
leases, remainder, err := ReadLeases(data)
|
||||
lease_set.LeaseList = leases
|
||||
|
||||
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
|
||||
|
||||
*/
|
@ -2,15 +2,15 @@ package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func buildDestination() RouterIdentity {
|
||||
router_ident_data := make([]byte, 128+256)
|
||||
router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
|
||||
return RouterIdentity(router_ident_data)
|
||||
rri, _, _ := ReadRouterIdentity(router_ident_data)
|
||||
return rri
|
||||
}
|
||||
|
||||
func buildPublicKey() []byte {
|
||||
@ -55,33 +55,34 @@ func buildSignature(size int) []byte {
|
||||
|
||||
func buildFullLeaseSet(n int) LeaseSet {
|
||||
lease_set_data := make([]byte, 0)
|
||||
lease_set_data = append(lease_set_data, buildDestination()...)
|
||||
lease_set_data = append(lease_set_data, buildDestination().Bytes()...)
|
||||
lease_set_data = append(lease_set_data, buildPublicKey()...)
|
||||
lease_set_data = append(lease_set_data, buildSigningKey()...)
|
||||
lease_set_data = append(lease_set_data, byte(n))
|
||||
lease_set_data = append(lease_set_data, buildLease(n)...)
|
||||
lease_set_data = append(lease_set_data, buildSignature(64)...)
|
||||
return LeaseSet(lease_set_data)
|
||||
leaseSet, _, _ := ReadLeaseSet(lease_set_data)
|
||||
return leaseSet
|
||||
}
|
||||
|
||||
func TestDestinationIsCorrect(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
lease_set := buildFullLeaseSet(1)
|
||||
dest, err := lease_set.Destination()
|
||||
dest, err := lease_set.GetDestination()
|
||||
assert.Nil(err)
|
||||
dest_cert, err := dest.Certificate()
|
||||
assert.Nil(err)
|
||||
cert_type := dest_cert.Type()
|
||||
_, cert_bytes, err := dest_cert.Type()
|
||||
assert.Nil(err)
|
||||
assert.Equal(CERT_KEY, cert_type)
|
||||
assert.Equal(CERT_KEY, cert_bytes)
|
||||
}
|
||||
|
||||
func TestPublicKeyIsCorrect(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
lease_set := buildFullLeaseSet(1)
|
||||
pk, err := lease_set.PublicKey()
|
||||
pk, err := lease_set.GetPublicKey()
|
||||
if assert.Nil(err) {
|
||||
assert.Equal(
|
||||
0,
|
||||
@ -97,7 +98,7 @@ func TestSigningKeyIsCorrect(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
lease_set := buildFullLeaseSet(1)
|
||||
sk, err := lease_set.SigningKey()
|
||||
sk, err := lease_set.GetSigningKey()
|
||||
if assert.Nil(err) {
|
||||
assert.Equal(128, sk.Len())
|
||||
}
|
||||
@ -155,7 +156,7 @@ func TestLeasesHaveCorrectData(t *testing.T) {
|
||||
0,
|
||||
bytes.Compare(
|
||||
lease,
|
||||
leases[i][:],
|
||||
leases[i].Bytes()[:],
|
||||
),
|
||||
)
|
||||
}
|
||||
@ -167,7 +168,7 @@ func TestSignatureIsCorrect(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
lease_set := buildFullLeaseSet(1)
|
||||
sig, err := lease_set.Signature()
|
||||
sig, err := lease_set.GetSignature()
|
||||
if assert.Nil(err) {
|
||||
assert.Equal(
|
||||
0,
|
||||
|
@ -28,27 +28,25 @@ val_string :: String
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Mapping []byte
|
||||
|
||||
// Parsed key-values pairs inside a Mapping.
|
||||
type MappingValues [][2]I2PString
|
||||
type MappingValues [][2]String
|
||||
|
||||
//
|
||||
// Returns the values contained in a Mapping in the form of a MappingValues.
|
||||
//
|
||||
func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
||||
var str I2PString
|
||||
var str String
|
||||
var remainder = mapping
|
||||
var err error
|
||||
|
||||
l := NewInteger(remainder[:2])
|
||||
length := l.Int()
|
||||
inferred_length := length + 2
|
||||
length, err := NewInteger(remainder[:2])
|
||||
inferred_length := length.Value() + 2
|
||||
remainder = remainder[2:]
|
||||
mapping_len := len(mapping)
|
||||
if mapping_len > inferred_length {
|
||||
@ -74,7 +72,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
||||
for {
|
||||
// Read a key, breaking on fatal errors
|
||||
// and appending warnings
|
||||
str, remainder, err = ReadI2PString(remainder)
|
||||
str, remainder, err = ReadString(remainder)
|
||||
key_str := str
|
||||
if err != nil {
|
||||
if stopValueRead(err) {
|
||||
@ -94,7 +92,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
||||
|
||||
// Read a value, breaking on fatal errors
|
||||
// and appending warnings
|
||||
str, remainder, err = ReadI2PString(remainder)
|
||||
str, remainder, err = ReadString(remainder)
|
||||
val_str := str
|
||||
if err != nil {
|
||||
if stopValueRead(err) {
|
||||
@ -113,7 +111,7 @@ func (mapping Mapping) Values() (map_values MappingValues, errs []error) {
|
||||
remainder = remainder[1:]
|
||||
|
||||
// Append the key-value pair and break if there is no more data to read
|
||||
map_values = append(map_values, [2]I2PString{key_str, val_str})
|
||||
map_values = append(map_values, [2]String{key_str, val_str})
|
||||
if len(remainder) == 0 {
|
||||
break
|
||||
}
|
||||
@ -176,7 +174,7 @@ func GoMapToMapping(gomap map[string]string) (mapping Mapping, err error) {
|
||||
}
|
||||
map_vals = append(
|
||||
map_vals,
|
||||
[2]I2PString{key_str, val_str},
|
||||
[2]String{key_str, val_str},
|
||||
)
|
||||
}
|
||||
mapping = ValuesToMapping(map_vals)
|
||||
|
@ -3,9 +3,8 @@ package common
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValuesExclusesPairWithBadData(t *testing.T) {
|
||||
@ -117,10 +116,10 @@ func TestMappingOrderSortsValuesThenKeys(t *testing.T) {
|
||||
a, _ := ToI2PString("a")
|
||||
b, _ := ToI2PString("b")
|
||||
values := MappingValues{
|
||||
[2]I2PString{b, b},
|
||||
[2]I2PString{b, a},
|
||||
[2]I2PString{a, b},
|
||||
[2]I2PString{a, a},
|
||||
[2]String{b, b},
|
||||
[2]String{b, a},
|
||||
[2]String{a, b},
|
||||
[2]String{a, a},
|
||||
}
|
||||
mappingOrder(values)
|
||||
for i, pair := range values {
|
||||
|
@ -37,7 +37,6 @@ options :: Mapping
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -52,13 +51,12 @@ type RouterAddress []byte
|
||||
// Return the cost integer for this RouterAddress and any errors encountered
|
||||
// parsing the RouterAddress.
|
||||
//
|
||||
func (router_address RouterAddress) Cost() (cost int, err error) {
|
||||
func (router_address RouterAddress) Cost() (cost *Integer, err error) {
|
||||
err, exit := router_address.checkValid()
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
c := NewInteger([]byte{router_address[0]})
|
||||
cost = c.Int()
|
||||
cost, err = NewInteger([]byte{router_address[0]})
|
||||
return
|
||||
}
|
||||
|
||||
@ -79,12 +77,12 @@ func (router_address RouterAddress) Expiration() (date Date, err error) {
|
||||
// Return the Transport type for this RouterAddress and any errors encountered
|
||||
// parsing the RouterAddress.
|
||||
//
|
||||
func (router_address RouterAddress) TransportStyle() (str I2PString, err error) {
|
||||
func (router_address RouterAddress) TransportStyle() (str String, err error) {
|
||||
err, exit := router_address.checkValid()
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
str, _, err = ReadI2PString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
|
||||
str, _, err = ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
|
||||
return
|
||||
}
|
||||
|
||||
@ -97,7 +95,7 @@ func (router_address RouterAddress) Options() (mapping Mapping, err error) {
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
_, remainder, err := ReadI2PString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
|
||||
_, remainder, err := ReadString(router_address[ROUTER_ADDRESS_MIN_SIZE:])
|
||||
if len(remainder) == 0 {
|
||||
return
|
||||
}
|
||||
@ -139,23 +137,22 @@ func ReadRouterAddress(data []byte) (router_address RouterAddress, remainder []b
|
||||
return
|
||||
}
|
||||
router_address = append(router_address, data[:ROUTER_ADDRESS_MIN_SIZE]...)
|
||||
str, remainder, err := ReadI2PString(data[ROUTER_ADDRESS_MIN_SIZE:])
|
||||
str, remainder, err := ReadString(data[ROUTER_ADDRESS_MIN_SIZE:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
router_address = append(router_address, str...)
|
||||
map_size := 0
|
||||
map_size := &Integer{}
|
||||
mapping := make([]byte, 0)
|
||||
if len(remainder) >= 2 {
|
||||
ms := NewInteger(remainder[:2])
|
||||
map_size = ms.Int()
|
||||
if len(remainder) < map_size+2 {
|
||||
map_size, err = NewInteger(remainder[:2])
|
||||
if len(remainder) < map_size.Value()+2 {
|
||||
err = errors.New("not enough data for map inside router address")
|
||||
router_address = RouterAddress([]byte{})
|
||||
remainder = []byte{}
|
||||
return
|
||||
}
|
||||
mapping = remainder[:map_size+2]
|
||||
mapping = remainder[:map_size.Value()+2]
|
||||
router_address = append(router_address, mapping...)
|
||||
}
|
||||
|
||||
|
@ -15,22 +15,27 @@ import (
|
||||
//
|
||||
// A RouterIdentity is identical to KeysAndCert.
|
||||
//
|
||||
type RouterIdentity []byte
|
||||
type RouterIdentity struct {
|
||||
KeysAndCert
|
||||
}
|
||||
|
||||
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) {
|
||||
return KeysAndCert(router_identity).SigningPublicKey()
|
||||
return router_identity.SigningPublicKey()
|
||||
}
|
||||
|
||||
func (router_identity RouterIdentity) Certificate() (*Certificate, error) {
|
||||
return KeysAndCert(router_identity).Certificate()
|
||||
func (router_identity RouterIdentity) Certificate() (Certificate, error) {
|
||||
return router_identity.Certificate()
|
||||
}
|
||||
|
||||
func ReadRouterIdentity(data []byte) (router_identity RouterIdentity, remainder []byte, err error) {
|
||||
keys_and_cert, remainder, err := ReadKeysAndCert(data)
|
||||
router_identity = RouterIdentity(keys_and_cert)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
router_identity.KeysAndCert = keys_and_cert
|
||||
return
|
||||
}
|
||||
|
@ -75,7 +75,6 @@ signature :: Signature
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -98,7 +97,7 @@ func (router_info RouterInfo) IdentHash() (h Hash, err error) {
|
||||
var ri RouterIdentity
|
||||
ri, err = router_info.RouterIdentity()
|
||||
if err == nil {
|
||||
h = HashData(ri)
|
||||
h = HashData(ri.Bytes())
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -129,7 +128,7 @@ func (router_info RouterInfo) Published() (date Date, err error) {
|
||||
//
|
||||
// Return the Integer representing the number of RouterAddresses that are contained in this RouterInfo.
|
||||
//
|
||||
func (router_info RouterInfo) RouterAddressCount() (count int, err error) {
|
||||
func (router_info RouterInfo) RouterAddressCount() (count *Integer, err error) {
|
||||
_, remainder, err := ReadRouterIdentity(router_info)
|
||||
if err != nil {
|
||||
return
|
||||
@ -145,8 +144,7 @@ func (router_info RouterInfo) RouterAddressCount() (count int, err error) {
|
||||
err = errors.New("error parsing router addresses: not enough data")
|
||||
return
|
||||
}
|
||||
c := NewInteger([]byte{remainder[8]})
|
||||
count = c.Int()
|
||||
count, err = NewInteger([]byte{remainder[8]})
|
||||
return
|
||||
}
|
||||
|
||||
@ -177,7 +175,7 @@ func (router_info RouterInfo) RouterAddresses() (router_addresses []RouterAddres
|
||||
err = cerr
|
||||
return
|
||||
}
|
||||
for i := 0; i < addr_count; i++ {
|
||||
for i := 0; i < addr_count.Value(); i++ {
|
||||
router_address, remaining, err = ReadRouterAddress(remaining)
|
||||
if err == nil {
|
||||
router_addresses = append(router_addresses, router_address)
|
||||
@ -200,7 +198,7 @@ func (router_info RouterInfo) PeerSize() int {
|
||||
//
|
||||
func (router_info RouterInfo) Options() (mapping Mapping) {
|
||||
head := router_info.optionsLocation()
|
||||
size := head + router_info.optionsSize()
|
||||
size := head + router_info.optionsSize().Value()
|
||||
mapping = Mapping(router_info[head:size])
|
||||
return
|
||||
}
|
||||
@ -210,9 +208,9 @@ func (router_info RouterInfo) Options() (mapping Mapping) {
|
||||
//
|
||||
func (router_info RouterInfo) Signature() (signature Signature) {
|
||||
head := router_info.optionsLocation()
|
||||
size := head + router_info.optionsSize()
|
||||
size := head + router_info.optionsSize().Value()
|
||||
ident, _ := router_info.RouterIdentity()
|
||||
keyCert, _ := NewKeyCertificate(ident)
|
||||
keyCert := ident.CertificateInterface //KeyCertificate(ident)
|
||||
sigSize := keyCert.SignatureSize()
|
||||
signature = Signature(router_info[size : size+sigSize])
|
||||
return
|
||||
@ -226,7 +224,7 @@ func (router_info RouterInfo) optionsLocation() (location int) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
location += len(data)
|
||||
location += len(data.Bytes())
|
||||
|
||||
remainder_len := len(remainder)
|
||||
if remainder_len < 9 {
|
||||
@ -249,7 +247,7 @@ func (router_info RouterInfo) optionsLocation() (location int) {
|
||||
err = cerr
|
||||
return
|
||||
}
|
||||
for i := 0; i < addr_count; i++ {
|
||||
for i := 0; i < addr_count.Value(); i++ {
|
||||
router_address, remaining, err = ReadRouterAddress(remaining)
|
||||
if err == nil {
|
||||
location += len(router_address)
|
||||
@ -263,9 +261,8 @@ func (router_info RouterInfo) optionsLocation() (location int) {
|
||||
//
|
||||
// Used during parsing to determine the size of the options in the RouterInfo.
|
||||
//
|
||||
func (router_info RouterInfo) optionsSize() (size int) {
|
||||
func (router_info RouterInfo) optionsSize() (size *Integer) {
|
||||
head := router_info.optionsLocation()
|
||||
s := NewInteger(router_info[head : head+2])
|
||||
size = s.Int() + 2
|
||||
size, _ = NewInteger(router_info[head : head+2]) //+ 2
|
||||
return
|
||||
}
|
||||
|
@ -10,7 +10,11 @@ import (
|
||||
func buildRouterIdentity() RouterIdentity {
|
||||
router_ident_data := make([]byte, 128+256)
|
||||
router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...)
|
||||
return RouterIdentity(router_ident_data)
|
||||
b, _, err := ReadRouterIdentity(router_ident_data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func buildDate() []byte {
|
||||
@ -33,7 +37,7 @@ func buildRouterAddress(transport string) RouterAddress {
|
||||
|
||||
func buildFullRouterInfo() RouterInfo {
|
||||
router_info_data := make([]byte, 0)
|
||||
router_info_data = append(router_info_data, buildRouterIdentity()...)
|
||||
router_info_data = append(router_info_data, buildRouterIdentity().Bytes()...)
|
||||
router_info_data = append(router_info_data, buildDate()...)
|
||||
router_info_data = append(router_info_data, 0x01)
|
||||
router_info_data = append(router_info_data, buildRouterAddress("foo")...)
|
||||
@ -116,7 +120,7 @@ func TestRouterAddressesReturnsAddressesWithMultiple(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
router_info_data := make([]byte, 0)
|
||||
router_info_data = append(router_info_data, buildRouterIdentity()...)
|
||||
router_info_data = append(router_info_data, buildRouterIdentity().Bytes()...)
|
||||
router_info_data = append(router_info_data, buildDate()...)
|
||||
router_info_data = append(router_info_data, 0x03)
|
||||
router_info_data = append(router_info_data, buildRouterAddress("foo0")...)
|
||||
@ -184,8 +188,8 @@ func TestRouterIdentityIsCorrect(t *testing.T) {
|
||||
assert.Equal(
|
||||
0,
|
||||
bytes.Compare(
|
||||
[]byte(buildRouterIdentity()),
|
||||
[]byte(router_identity),
|
||||
[]byte(buildRouterIdentity().Bytes()),
|
||||
[]byte(router_identity.Bytes()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package common
|
||||
|
||||
/*
|
||||
I2P I2PString
|
||||
I2P String
|
||||
https://geti2p.net/spec/common-structures#string
|
||||
Accurate for version 0.9.24
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -17,28 +16,27 @@ const (
|
||||
STRING_MAX_SIZE = 255
|
||||
)
|
||||
|
||||
type I2PString []byte
|
||||
type String []byte
|
||||
|
||||
//
|
||||
// Look up the length of the string, reporting errors if the string is
|
||||
// invalid or the specified length does not match the provided data.
|
||||
//
|
||||
func (str I2PString) Length() (length int, err error) {
|
||||
func (str String) Length() (length *Integer, err error) {
|
||||
if len(str) == 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(I2PString) Length",
|
||||
"at": "(String) Length",
|
||||
"reason": "no data",
|
||||
}).Error("error parsing string")
|
||||
err = errors.New("error parsing string: zero length")
|
||||
return
|
||||
}
|
||||
l := NewInteger([]byte{byte(str[0])})
|
||||
length = l.Int()
|
||||
inferred_len := length + 1
|
||||
length, err = NewInteger([]byte{byte(str[0])})
|
||||
inferred_len := length.Value() + 1
|
||||
str_len := len(str)
|
||||
if inferred_len > str_len {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(I2PString) Length",
|
||||
"at": "(String) Length",
|
||||
"string_bytes_length": str_len,
|
||||
"string_length_field": length,
|
||||
"expected_bytes_length": inferred_len,
|
||||
@ -47,7 +45,7 @@ func (str I2PString) Length() (length int, err error) {
|
||||
err = errors.New("string parsing warning: string data is shorter than specified by length")
|
||||
} else if str_len > inferred_len {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "(I2PString) Length",
|
||||
"at": "(String) Length",
|
||||
"string_bytes_length": str_len,
|
||||
"string_length_field": length,
|
||||
"expected_bytes_length": inferred_len,
|
||||
@ -61,7 +59,7 @@ func (str I2PString) Length() (length int, err error) {
|
||||
//
|
||||
// Return the string data and any errors encountered by Length.
|
||||
//
|
||||
func (str I2PString) Data() (data string, err error) {
|
||||
func (str String) Data() (data string, err error) {
|
||||
length, err := str.Length()
|
||||
if err != nil {
|
||||
switch err.Error() {
|
||||
@ -71,7 +69,7 @@ func (str I2PString) Data() (data string, err error) {
|
||||
data = string(str[1:])
|
||||
return
|
||||
case "string parsing warning: string contains data beyond length":
|
||||
data = string(str[1 : length+1])
|
||||
data = string(str[1 : length.Value()+1])
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -80,14 +78,14 @@ func (str I2PString) Data() (data string, err error) {
|
||||
}
|
||||
|
||||
//
|
||||
// This function takes an unformatted Go string and returns a I2PString
|
||||
// This function takes an unformatted Go string and returns a String
|
||||
// and any errors encountered during the encoding.
|
||||
//
|
||||
func ToI2PString(data string) (str I2PString, err error) {
|
||||
func ToI2PString(data string) (str String, err error) {
|
||||
data_len := len(data)
|
||||
if data_len > STRING_MAX_SIZE {
|
||||
log.WithFields(log.Fields{
|
||||
"at": "ToI2PI2PString",
|
||||
"at": "ToI2PString",
|
||||
"string_len": data_len,
|
||||
"max_len": STRING_MAX_SIZE,
|
||||
"reason": "too much data",
|
||||
@ -97,20 +95,20 @@ func ToI2PString(data string) (str I2PString, err error) {
|
||||
}
|
||||
i2p_string := []byte{byte(data_len)}
|
||||
i2p_string = append(i2p_string, []byte(data)...)
|
||||
str = I2PString(i2p_string)
|
||||
str = String(i2p_string)
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
// Read a string from a slice of bytes, returning any extra data on the end
|
||||
// of the slice and any errors encountered parsing the I2PString.
|
||||
// of the slice and any errors encountered parsing the String.
|
||||
//
|
||||
func ReadI2PString(data []byte) (str I2PString, remainder []byte, err error) {
|
||||
str = I2PString(data)
|
||||
length, err := I2PString(data).Length()
|
||||
func ReadString(data []byte) (str String, remainder []byte, err error) {
|
||||
str = String(data)
|
||||
length, err := String(data).Length()
|
||||
if err != nil && err.Error() == "string parsing warning: string contains data beyond length" {
|
||||
str = I2PString(data[:length+1])
|
||||
remainder = data[length+1:]
|
||||
str = String(data[:length.Value()+1])
|
||||
remainder = data[length.Value()+1:]
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
|
@ -1,24 +1,23 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringReportsCorrectLength(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
str_len, err := I2PString([]byte{0x02, 0x00, 0x00}).Length()
|
||||
str_len, err := String([]byte{0x02, 0x00, 0x00}).Length()
|
||||
|
||||
assert.Equal(str_len, 2, "Length() did not report correct length")
|
||||
assert.Nil(err, "Length() reported an error on valid string")
|
||||
}
|
||||
|
||||
func TestI2PStringReportsLengthZeroError(t *testing.T) {
|
||||
func TestStringReportsLengthZeroError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
str_len, err := I2PString(make([]byte, 0)).Length()
|
||||
str_len, err := String(make([]byte, 0)).Length()
|
||||
|
||||
assert.Equal(str_len, 0, "Length() reported non-zero length on empty slice")
|
||||
if assert.NotNil(err) {
|
||||
@ -26,10 +25,10 @@ func TestI2PStringReportsLengthZeroError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestI2PStringReportsExtraDataError(t *testing.T) {
|
||||
func TestStringReportsExtraDataError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
str_len, err := I2PString([]byte{0x01, 0x00, 0x00}).Length()
|
||||
str_len, err := String([]byte{0x01, 0x00, 0x00}).Length()
|
||||
|
||||
assert.Equal(str_len, 1, "Length() reported wrong size when extra data present")
|
||||
if assert.NotNil(err) {
|
||||
@ -37,10 +36,10 @@ func TestI2PStringReportsExtraDataError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestI2PStringDataReportsLengthZeroError(t *testing.T) {
|
||||
func TestStringDataReportsLengthZeroError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
str_len, err := I2PString([]byte{0x01}).Length()
|
||||
str_len, err := String([]byte{0x01}).Length()
|
||||
|
||||
assert.Equal(str_len, 1, "Length() reported wrong size with missing data")
|
||||
if assert.NotNil(err) {
|
||||
@ -48,10 +47,10 @@ func TestI2PStringDataReportsLengthZeroError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestI2PStringDataReportsExtraDataError(t *testing.T) {
|
||||
func TestStringDataReportsExtraDataError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := I2PString([]byte{0x01, 0x00, 0x01}).Data()
|
||||
data, err := String([]byte{0x01, 0x00, 0x01}).Data()
|
||||
data_len := len(data)
|
||||
|
||||
assert.Equal(data_len, 1, "Data() reported wrong size on string with extra data")
|
||||
@ -60,10 +59,10 @@ func TestI2PStringDataReportsExtraDataError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestI2PStringDataEmptyWhenZeroLength(t *testing.T) {
|
||||
func TestStringDataEmptyWhenZeroLength(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := I2PString(make([]byte, 0)).Data()
|
||||
data, err := String(make([]byte, 0)).Data()
|
||||
|
||||
assert.Equal(len(data), 0, "Data() returned data when none was present:")
|
||||
if assert.NotNil(err) {
|
||||
@ -71,10 +70,10 @@ func TestI2PStringDataEmptyWhenZeroLength(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestI2PStringDataErrorWhenNonZeroLengthOnly(t *testing.T) {
|
||||
func TestStringDataErrorWhenNonZeroLengthOnly(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
data, err := I2PString([]byte{0x01}).Data()
|
||||
data, err := String([]byte{0x01}).Data()
|
||||
|
||||
assert.Equal(len(data), 0, "Data() returned data when only length was present")
|
||||
if assert.NotNil(err) {
|
||||
@ -82,7 +81,7 @@ func TestI2PStringDataErrorWhenNonZeroLengthOnly(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestToI2PI2PStringFormatsCorrectly(t *testing.T) {
|
||||
func TestToI2PStringFormatsCorrectly(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
i2p_string, err := ToI2PString(string([]byte{0x08, 0x09}))
|
||||
@ -112,38 +111,38 @@ func TestReadStringReadsLength(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := []byte{0x01, 0x04, 0x06}
|
||||
str, remainder, err := ReadI2PString(bytes)
|
||||
str, remainder, err := ReadString(bytes)
|
||||
|
||||
assert.Nil(err, "ReadI2PString() returned error reading string with extra data")
|
||||
assert.Equal(len(str), 2, "ReadI2PString() did not return correct string length")
|
||||
assert.Equal(1, int(str[0]), "ReadI2PString() did not return correct string")
|
||||
assert.Equal(4, int(str[1]), "ReadI2PString() did not return correct string")
|
||||
assert.Equal(len(remainder), 1, "ReadI2PString() did not return correct remainder length")
|
||||
assert.Equal(6, int(remainder[0]), "ReadI2PString() did not return correct remainder")
|
||||
assert.Nil(err, "ReadString() returned error reading string with extra data")
|
||||
assert.Equal(len(str), 2, "ReadString() did not return correct string length")
|
||||
assert.Equal(1, int(str[0]), "ReadString() did not return correct string")
|
||||
assert.Equal(4, int(str[1]), "ReadString() did not return correct string")
|
||||
assert.Equal(len(remainder), 1, "ReadString() did not return correct remainder length")
|
||||
assert.Equal(6, int(remainder[0]), "ReadString() did not return correct remainder")
|
||||
}
|
||||
|
||||
func TestReadI2PStringErrWhenEmptySlice(t *testing.T) {
|
||||
func TestReadStringErrWhenEmptySlice(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bytes := make([]byte, 0)
|
||||
_, _, err := ReadI2PString(bytes)
|
||||
_, _, err := ReadString(bytes)
|
||||
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal(err.Error(), "error parsing string: zero length", "correct error message should be returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadI2PStringErrWhenDataTooShort(t *testing.T) {
|
||||
func TestReadStringErrWhenDataTooShort(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
short_str := []byte{0x03, 0x01}
|
||||
str, remainder, err := ReadI2PString(short_str)
|
||||
str, remainder, err := ReadString(short_str)
|
||||
|
||||
if assert.NotNil(err) {
|
||||
assert.Equal(err.Error(), "string parsing warning: string data is shorter than specified by length", "correct error message should be returned")
|
||||
}
|
||||
assert.Equal(len(str), 2, "ReadI2PString() did not return the slice as string when too long")
|
||||
assert.Equal(3, int(str[0]), "ReadI2PString() did not return the correct partial string")
|
||||
assert.Equal(1, int(str[1]), "ReadI2PString() did not return the correct partial string")
|
||||
assert.Equal(len(remainder), 0, "ReadI2PString() returned a remainder when the string data was too short")
|
||||
assert.Equal(len(str), 2, "ReadString() did not return the slice as string when too long")
|
||||
assert.Equal(3, int(str[0]), "ReadString() did not return the correct partial string")
|
||||
assert.Equal(1, int(str[1]), "ReadString() did not return the correct partial string")
|
||||
assert.Equal(len(remainder), 0, "ReadString() returned a remainder when the string data was too short")
|
||||
}
|
||||
|
@ -1,37 +1,54 @@
|
||||
package crypto
|
||||
|
||||
/*
|
||||
#cgo pkg-config: libsodium
|
||||
#include <sodium.h>
|
||||
#include <stdint.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Ed25519PublicKey []byte
|
||||
type Ed25519PublicKey [32]byte
|
||||
|
||||
type Ed25519Verifier struct {
|
||||
k []byte
|
||||
k [32]C.uchar
|
||||
}
|
||||
|
||||
func (k Ed25519PublicKey) NewVerifier() (v Verifier, err error) {
|
||||
temp := new(Ed25519Verifier)
|
||||
temp.k = k
|
||||
v = temp
|
||||
return temp, nil
|
||||
ev := new(Ed25519Verifier)
|
||||
for i, b := range k {
|
||||
ev.k[i] = C.uchar(b)
|
||||
}
|
||||
v = ev
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Ed25519Verifier) VerifyHash(h, sig []byte) (err error) {
|
||||
if len(sig) != ed25519.SignatureSize {
|
||||
if len(sig) == C.crypto_sign_BYTES {
|
||||
// valid size of sig
|
||||
// copy signature and hash
|
||||
var csig, ch [32]C.uchar
|
||||
for i, b := range h {
|
||||
ch[i] = C.uchar(b)
|
||||
}
|
||||
for i, b := range sig {
|
||||
csig[i] = C.uchar(b)
|
||||
}
|
||||
// verify
|
||||
if C.crypto_sign_verify_detached(&csig[0], &ch[0], C.ulonglong(32), &v.k[0]) == 0 {
|
||||
// valid signature
|
||||
} else {
|
||||
// bad signature
|
||||
err = ErrInvalidSignature
|
||||
}
|
||||
} else {
|
||||
// bad size of sig
|
||||
err = ErrBadSignatureSize
|
||||
return
|
||||
}
|
||||
if len(v.k) != ed25519.PublicKeySize {
|
||||
err = errors.New("failed to verify: invalid ed25519 public key size")
|
||||
return
|
||||
}
|
||||
|
||||
ok := ed25519.Verify(v.k, h, sig)
|
||||
if !ok {
|
||||
err = errors.New("failed to verify: invalid signature")
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -42,23 +59,35 @@ func (v *Ed25519Verifier) Verify(data, sig []byte) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type Ed25519PrivateKey ed25519.PrivateKey
|
||||
type Ed25519PrivateKey [32]byte
|
||||
|
||||
type Ed25519Signer struct {
|
||||
k []byte
|
||||
k [32]C.uchar
|
||||
}
|
||||
|
||||
func (s *Ed25519Signer) Sign(data []byte) (sig []byte, err error) {
|
||||
if len(s.k) != ed25519.PrivateKeySize {
|
||||
err = errors.New("failed to sign: invalid ed25519 private key size")
|
||||
return
|
||||
}
|
||||
h := sha512.Sum512(data)
|
||||
sig, err = s.SignHash(h[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Ed25519Signer) SignHash(h []byte) (sig []byte, err error) {
|
||||
sig = ed25519.Sign(s.k, h)
|
||||
var ch [32]C.uchar
|
||||
for i, b := range h {
|
||||
ch[i] = C.uchar(b)
|
||||
}
|
||||
var csig [32]C.uchar
|
||||
var smlen_p C.ulonglong
|
||||
res := C.crypto_sign_detached(&csig[0], &smlen_p, &ch[0], C.ulonglong(32), &s.k[0])
|
||||
if res == 0 {
|
||||
// success signing
|
||||
sig = make([]byte, 32)
|
||||
for i, b := range csig {
|
||||
sig[i] = byte(b)
|
||||
}
|
||||
} else {
|
||||
// failed signing
|
||||
err = errors.New(fmt.Sprintf("failed to sign: crypto_sign_detached exit code %d", int(res)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -1,42 +1,9 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEd25519(t *testing.T) {
|
||||
var pubKey Ed25519PublicKey
|
||||
|
||||
signer := new(Ed25519Signer)
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Log("Failed to generate ed25519 test key")
|
||||
t.Fail()
|
||||
}
|
||||
pubKey = []byte(pub)
|
||||
signer.k = []byte(priv)
|
||||
|
||||
message := make([]byte, 123)
|
||||
io.ReadFull(rand.Reader, message)
|
||||
|
||||
sig, err := signer.Sign(message)
|
||||
if err != nil {
|
||||
t.Log("Failed to sign message")
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
verifier, err := pubKey.NewVerifier()
|
||||
if err != nil {
|
||||
t.Logf("Error from verifier: %s", err)
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
err = verifier.Verify(message, sig)
|
||||
if err != nil {
|
||||
t.Log("Failed to verify message")
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user