Finished Crypto

This commit is contained in:
Willie Koomson
2018-03-05 23:19:47 -05:00
parent b785dba791
commit 4f9daea6f6
5 changed files with 362 additions and 3 deletions

61
certificate.go Normal file
View File

@ -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)
}

217
crypto.go Normal file
View File

@ -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
}

View File

@ -9,6 +9,5 @@ func Deinit() {
}
const (
CLIENT uint32 = (iota +1) << 8
)
CLIENT uint32 = (iota + 1) << 8
)

10
session.go Normal file
View File

@ -0,0 +1,10 @@
package go_i2cp
import "bytes"
type Session struct {
}
func (session Session) dispatchMessage(protocol uint8, srcPort, destPort uint16, payload *Stream) {
}

72
version.go Normal file
View File

@ -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
}