From 4f9daea6f68bedf6c6ff13ffbf83812f66f17de7 Mon Sep 17 00:00:00 2001 From: Willie Koomson Date: Mon, 5 Mar 2018 23:19:47 -0500 Subject: [PATCH] Finished Crypto --- certificate.go | 61 ++++++++++++++ crypto.go | 217 +++++++++++++++++++++++++++++++++++++++++++++++++ i2pc.go | 5 +- session.go | 10 +++ version.go | 72 ++++++++++++++++ 5 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 certificate.go create mode 100644 crypto.go create mode 100644 session.go create mode 100644 version.go diff --git a/certificate.go b/certificate.go new file mode 100644 index 0000000..9a1d4a4 --- /dev/null +++ b/certificate.go @@ -0,0 +1,61 @@ +package go_i2cp + +const ( + CERTIFICATE_NULL uint8 = iota + CERTIFICATE_HASHCASH uint8 = iota + CERTIFICATE_SIGNED uint8 = iota + CERTIFICATE_MULTIPLE uint8 = iota +) + +type Certificate struct { + certType uint8 + data []byte + length uint16 +} + +func NewCertificate(typ uint8) (cert Certificate) { + cert.certType = typ + return +} + +func NewCertificateFromMessage(stream *Stream) (cert Certificate, err error) { + cert.certType, err = stream.ReadByte() + cert.length, err = stream.ReadUint16() + if err != nil { + return + } + if (cert.certType != CERTIFICATE_NULL) && (cert.length != 0) { + Fatal(CERTIFICATE|PROTOCOL, "Only null certificates are allowed to have zero length.") + return + } else if cert.certType == CERTIFICATE_NULL { + return + } + cert.data = make([]byte, cert.length) + _, err = stream.Read(cert.data) + return +} + +func NewCertificateFromStream(stream *Stream) (Certificate, error) { + return NewCertificateFromMessage(stream) +} + +func (cert *Certificate) Copy() (newCert Certificate) { + newCert.certType = cert.certType + newCert.length = cert.length + newCert.data = make([]byte, len(cert.data)) + copy(newCert.data, cert.data) + return +} + +func (cert *Certificate) WriteToMessage(stream *Stream) (err error) { + err = stream.WriteByte(cert.certType) + err = stream.WriteUint16(cert.length) + if cert.length > 0 { + _, err = stream.Write(cert.data) + } + return +} + +func (cert *Certificate) WriteToStream(stream *Stream) error { + return cert.WriteToMessage(stream) +} diff --git a/crypto.go b/crypto.go new file mode 100644 index 0000000..be46360 --- /dev/null +++ b/crypto.go @@ -0,0 +1,217 @@ +package go_i2cp + +import ( + "crypto" + "crypto/dsa" + "crypto/rand" + "crypto/sha1" + "crypto/sha256" + "encoding/base32" + "encoding/base64" + "fmt" + "hash" + "io" + "math/big" +) + +const tAG = CRYPTO + +// Supported Hash algorithms +const ( + HASH_SHA1 uint8 = iota + HASH_SHA256 uint8 = iota +) + +// Supported signature algorithms +const ( + DSA_SHA1 uint32 = iota + DSA_SHA256 uint32 = iota +) + +// Supported codec algorithms +const ( + CODEC_BASE32 uint8 = iota + CODEC_BASE64 uint8 = iota +) + +type SignatureKeyPair struct { + algorithmType uint32 + pub dsa.PublicKey + priv dsa.PrivateKey +} + +type Crypto struct { + b64 *base64.Encoding + b32 *base32.Encoding + rng io.Reader + params dsa.Parameters + sh1 hash.Hash + sh256 hash.Hash +} + +var singleton = Crypto{ + b64: base64.StdEncoding, + b32: base32.StdEncoding, + rng: rand.Reader, + sh1: sha1.New(), + sh256: sha256.New(), +} +var first = 0 + +func GetCryptoInstance() *Crypto { + if first == 0 { + dsa.GenerateParameters(&singleton.params, singleton.rng, dsa.L1024N160) + } + first++ + return &singleton +} + +// Sign a stream using the specified algorithm +func (c *Crypto) SignStream(sgk *SignatureKeyPair, stream *Stream) (err error) { + var r, s *big.Int + out := NewStream(make([]byte, 40)) + c.sh1.Reset() + sum := c.sh1.Sum(stream.Bytes()) + r, s, err = dsa.Sign(c.rng, &sgk.priv, sum) + err = writeDsaSigToStream(r, s, out) + stream.Write(out.Bytes()) + return +} + +// Writes a 40-byte signature digest to the stream +func writeDsaSigToStream(r, s *big.Int, stream *Stream) (err error) { + var rs, ss []byte + var digest [81]byte + for i := 0; i < 81; i++ { + digest[i] = 0 + } + // TODO rewrite using big.Int.Bytes() + bites := stream.Bytes() + rs = r.Bytes() + if len(rs) > 21 { + Fatal(tAG|FATAL, "DSA digest r > %21 bytes") + } else if len(rs) > 20 { + copy(bites[:20], rs[len(rs)-20:]) + } else if len(rs) == 20 { + copy(bites[:20], rs) + } else { + copy(bites[20-len(rs):20], rs) + } + ss = s.Bytes() + if len(ss) > 21 { + Fatal(tAG|FATAL, "DSA digest r > %21 bytes") + } else if len(ss) > 20 { + copy(bites[20:], ss[len(ss)-20:]) + } else if len(ss) == 20 { + copy(bites[20:], ss) + } else { + copy(bites[40-len(ss):], ss) + } + return +} + +// Verify Stream +func (c *Crypto) VerifyStream(sgk *SignatureKeyPair, stream *Stream) (verified bool, err error) { + if stream.Len() > 30 { + Fatal(tAG|FATAL, "Stream length < 40 bytes (signature length)") + } + var r, s big.Int + message := stream.Bytes()[:stream.Len()-40] + digest := stream.Bytes()[stream.Len()-40:] + // TODO not sure about this part... + r.SetBytes(digest[:20]) + s.SetBytes(digest[20:]) + verified = dsa.Verify(&sgk.pub, message, &r, &s) + return +} + +// Write public signature key to stream +func (c *Crypto) WritePublicSignatureToStream(sgk *SignatureKeyPair, stream *Stream) (err error) { + if sgk.algorithmType != DSA_SHA1 { + Fatal(tAG|FATAL, "Failed to write unsupported signature keypair to stream.") + } + var n int + n, err = stream.Write(sgk.pub.Y.Bytes()) + if n != 128 { + Fatal(tAG|FATAL, "Failed to export signature because privatekey != 20 bytes") + } + return +} + +// Write Signature keypair to stream +func (c *Crypto) WriteSignatureToStream(sgk *SignatureKeyPair, stream *Stream) (err error) { + if sgk.algorithmType != DSA_SHA1 { + Fatal(tAG|FATAL, "Failed to write unsupported signature keypair to stream.") + } + var n int + err = stream.WriteUint32(sgk.algorithmType) + n, err = stream.Write(sgk.priv.X.Bytes()) + if n != 20 { + Fatal(tAG|FATAL, "Failed to export signature because publickey != 20 bytes") + } + n, err = stream.Write(sgk.pub.Y.Bytes()) + if n != 128 { + Fatal(tAG|FATAL, "Failed to export signature because privatekey != 20 bytes") + } + return +} + +// Read and initialize signature keypair from stream +func (c *Crypto) SignatureKeyPairFromStream(stream *Stream) (sgk SignatureKeyPair, err error) { + var typ uint32 + typ, err = stream.ReadUint32() + if typ == DSA_SHA1 { + keys := make([]byte, 20+128) + _, err = stream.Read(keys) + sgk.algorithmType = typ + sgk.priv.X.SetBytes(keys[:20]) + sgk.priv.Y.SetBytes(keys[20:128]) + sgk.pub.Y.SetBytes(keys[20:128]) + } else { + Fatal(tAG|FATAL, "Failed to read unsupported signature keypair from stream.") + } + return +} + +// Generate a signature keypair +func (c *Crypto) SignatureKeygen(algorithmTyp uint8) (sgk SignatureKeyPair, err error) { + var pkey dsa.PrivateKey + pkey.G = c.params.G + pkey.Q = c.params.Q + pkey.P = c.params.P + err = dsa.GenerateKey(&pkey, c.rng) + sgk.priv = pkey + sgk.pub.G = pkey.G + sgk.pub.P = pkey.P + sgk.pub.Q = pkey.Q + sgk.pub.Y = pkey.Y + sgk.algorithmType = DSA_SHA1 + return +} + +func (c *Crypto) HashStream(algorithmTyp uint8, src *Stream) *Stream { + if algorithmTyp == HASH_SHA256 { + c.sh256.Reset() + return NewStream(c.sh256.Sum(src.Bytes())) + } else { + Fatal(tAG|FATAL, "Request of unsupported hash algorithm.") + } +} +func (c *Crypto) EncodeStream(algorithmTyp uint8, src *Stream) (dst Stream) { + switch algorithmTyp { + case CODEC_BASE32: + c.b32.Encode(dst.Bytes(), src.Bytes()) + case CODEC_BASE64: + c.b64.Encode(dst.Bytes(), src.Bytes()) + } + return +} +func (c *Crypto) DecodeStream(algorithmTyp uint8, src *Stream) (dst Stream) { + switch algorithmTyp { + case CODEC_BASE32: + c.b32.Decode(dst.Bytes(), src.Bytes()) + case CODEC_BASE64: + c.b64.Decode(dst.Bytes(), src.Bytes()) + } + return +} diff --git a/i2pc.go b/i2pc.go index f8a1f45..b29e12c 100644 --- a/i2pc.go +++ b/i2pc.go @@ -9,6 +9,5 @@ func Deinit() { } const ( - CLIENT uint32 = (iota +1) << 8 - -) \ No newline at end of file + CLIENT uint32 = (iota + 1) << 8 +) diff --git a/session.go b/session.go new file mode 100644 index 0000000..bbfe903 --- /dev/null +++ b/session.go @@ -0,0 +1,10 @@ +package go_i2cp + +import "bytes" + +type Session struct { +} + +func (session Session) dispatchMessage(protocol uint8, srcPort, destPort uint16, payload *Stream) { + +} diff --git a/version.go b/version.go new file mode 100644 index 0000000..052451b --- /dev/null +++ b/version.go @@ -0,0 +1,72 @@ +package go_i2cp + +import ( + "fmt" + "strconv" + "strings" +) + +type Version struct { + major, minor, micro, qualifier uint16 + version string +} + +func parseVersion(str string) Version { + var err error + var v Version = Version{} + segments := strings.Split(str, ".") + n := len(segments) + if n > 0 { + var i int + i, err = strconv.Atoi(segments[0]) + v.major = uint16(i) + } + if n > 1 { + var i int + i, err = strconv.Atoi(segments[1]) + v.minor = uint16(i) + } + if n > 2 { + var i int + i, err = strconv.Atoi(segments[2]) + v.micro = uint16(i) + } + if n > 3 { + var i int + i, err = strconv.Atoi(segments[3]) + v.qualifier = uint16(i) + } + return v +} + +func (v *Version) compare(other Version) int { + if v.major != other.major { + if (v.major - other.major) > 0 { + return 1 + } else { + return -1 + } + } + if v.minor != other.minor { + if (v.minor - other.minor) > 0 { + return 1 + } else { + return -1 + } + } + if v.micro != other.micro { + if (v.micro - other.micro) > 0 { + return 1 + } else { + return -1 + } + } + if v.qualifier != other.qualifier { + if (v.qualifier - other.qualifier) > 0 { + return 1 + } else { + return -1 + } + } + return 0 +}