Files
go-i2cp/crypto.go

247 lines
6.1 KiB
Go
Raw Permalink Normal View History

2018-03-05 23:19:47 -05:00
package go_i2cp
import (
"crypto/dsa"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"errors"
2019-07-10 22:05:17 -04:00
"fmt"
2018-03-05 23:19:47 -05:00
"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 {
2019-07-10 22:05:17 -04:00
Fatal(tAG|FATAL, "DSA digest r > 21 bytes")
2018-03-05 23:19:47 -05:00
} 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 {
2019-07-10 22:05:17 -04:00
Fatal(tAG|FATAL, "DSA digest r > 21 bytes")
2018-03-05 23:19:47 -05:00
} 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) {
2019-07-10 22:05:17 -04:00
if sgk == nil {
Fatal(tAG|FATAL, "Error signature cannot be nil")
return fmt.Errorf("Error, signature cannot be nil")
}
if stream == nil {
Fatal(tAG|FATAL, "Error, stream cannot be nil")
return fmt.Errorf("Error, stream cannot be nil")
}
2018-03-05 23:19:47 -05:00
if sgk.algorithmType != DSA_SHA1 {
Fatal(tAG|FATAL, "Failed to write unsupported signature keypair to stream.")
2019-07-10 22:05:17 -04:00
return fmt.Errorf("Failed to write unsupported signature keypair to stream.")
2018-03-05 23:19:47 -05:00
}
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")
2019-07-10 22:05:17 -04:00
return err
2018-03-05 23:19:47 -05:00
}
n, err = stream.Write(sgk.pub.Y.Bytes())
if n != 128 {
Fatal(tAG|FATAL, "Failed to export signature because privatekey != 20 bytes")
2019-07-10 22:05:17 -04:00
return err
2018-03-05 23:19:47 -05:00
}
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
}
func (c *Crypto) PublicKeyFromStream(keyType uint32, stream *Stream) (key *big.Int, err error) {
if keyType == DSA_SHA1 {
key = &big.Int{}
keyBytes := make([]byte, 128)
_, err = stream.Read(keyBytes)
key.SetBytes(keyBytes)
return key, err
} else {
Fatal(CRYPTO, "Unknown signature algorithm")
return nil, errors.New("Unknown signature algorithm")
}
}
2018-03-05 23:19:47 -05:00
// Generate a signature keypair
2018-03-07 21:03:46 -05:00
func (c *Crypto) SignatureKeygen(algorithmTyp uint32) (sgk SignatureKeyPair, err error) {
2018-03-05 23:19:47 -05:00
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.")
return nil
2018-03-05 23:19:47 -05:00
}
}
func (c *Crypto) EncodeStream(algorithmTyp uint8, src *Stream) (dst *Stream) {
2018-03-05 23:19:47 -05:00
switch algorithmTyp {
case CODEC_BASE32:
dst = NewStream(make([]byte, c.b32.EncodedLen(src.Len())))
2018-03-05 23:19:47 -05:00
c.b32.Encode(dst.Bytes(), src.Bytes())
case CODEC_BASE64:
dst = NewStream(make([]byte, c.b64.EncodedLen(src.Len())))
2018-03-05 23:19:47 -05:00
c.b64.Encode(dst.Bytes(), src.Bytes())
}
return
}
func (c *Crypto) DecodeStream(algorithmTyp uint8, src *Stream) (dst *Stream, err error) {
2018-03-05 23:19:47 -05:00
switch algorithmTyp {
case CODEC_BASE32:
dst = NewStream(make([]byte, c.b32.DecodedLen(src.Len())))
2018-03-07 21:03:46 -05:00
_, err = c.b32.Decode(dst.Bytes(), src.Bytes())
2018-03-05 23:19:47 -05:00
case CODEC_BASE64:
dst = NewStream(make([]byte, c.b64.DecodedLen(src.Len())))
2018-03-07 21:03:46 -05:00
_, err = c.b64.Decode(dst.Bytes(), src.Bytes())
2018-03-05 23:19:47 -05:00
}
return
}