mirror of
https://github.com/number571/go-peer.git
synced 2026-09-12 19:50:00 +05:00
204 lines
4.8 KiB
Go
204 lines
4.8 KiB
Go
package message
|
|
|
|
import (
|
|
"bytes"
|
|
|
|
"github.com/number571/go-peer/pkg/crypto/hashing"
|
|
"github.com/number571/go-peer/pkg/crypto/keybuilder"
|
|
"github.com/number571/go-peer/pkg/crypto/puzzle"
|
|
"github.com/number571/go-peer/pkg/crypto/random"
|
|
"github.com/number571/go-peer/pkg/crypto/symmetric"
|
|
"github.com/number571/go-peer/pkg/encoding"
|
|
"github.com/number571/go-peer/pkg/payload"
|
|
)
|
|
|
|
const (
|
|
// Salt(cipher) + Salt(auth) + IV + Hash + Proof + PayloadSize + PayloadHead
|
|
cSaltSize = 16
|
|
CMessageHeadSize = 2*cSaltSize +
|
|
symmetric.CAESBlockSize +
|
|
hashing.CSHA256Size +
|
|
3*encoding.CSizeUint64
|
|
)
|
|
|
|
var (
|
|
_ IMessage = &sMessage{}
|
|
)
|
|
|
|
type sMessage struct {
|
|
fSalt [2][]byte // cipher_salt+auth_salt
|
|
fEncPPHP []byte // enc(proof_psize_hash_payload)
|
|
|
|
// not used in LoadMessage(), ToBytes(), ToString()
|
|
// used only in GetVoid(), GetHash(), in GetProof(), GetPayload()
|
|
fHash []byte
|
|
fVoid []byte
|
|
fProof uint64
|
|
fPayload payload.IPayload
|
|
}
|
|
|
|
func NewMessage(pSett ISettings, pPld payload.IPayload, pParallel, pLimitVoidSize uint64) IMessage {
|
|
payloadBytes := pPld.ToBytes()
|
|
payloadSize := encoding.Uint64ToBytes(uint64(len(payloadBytes)))
|
|
|
|
prng := random.NewStdPRNG()
|
|
|
|
randVoidSize := prng.GetUint64() % (pLimitVoidSize + 1)
|
|
voidBytes := prng.GetBytes(randVoidSize)
|
|
|
|
payloadVoidBytes := bytes.Join(
|
|
[][]byte{payloadBytes, voidBytes},
|
|
[]byte{},
|
|
)
|
|
|
|
authSalt := prng.GetBytes(cSaltSize)
|
|
hash := getAuthHash(pSett.GetNetworkKey(), authSalt, payloadVoidBytes)
|
|
proof := puzzle.NewPoWPuzzle(pSett.GetWorkSizeBits()).ProofBytes(hash, pParallel)
|
|
|
|
cipherSalt := prng.GetBytes(cSaltSize)
|
|
cipher := getCipher(pSett.GetNetworkKey(), cipherSalt)
|
|
proofBytes := encoding.Uint64ToBytes(proof)
|
|
|
|
return &sMessage{
|
|
fSalt: [2][]byte{
|
|
cipherSalt,
|
|
authSalt,
|
|
},
|
|
fEncPPHP: cipher.EncryptBytes(bytes.Join(
|
|
[][]byte{
|
|
proofBytes[:],
|
|
payloadSize[:],
|
|
hash,
|
|
payloadVoidBytes,
|
|
},
|
|
[]byte{},
|
|
)),
|
|
fHash: hash,
|
|
fVoid: voidBytes,
|
|
fProof: proof,
|
|
fPayload: pPld,
|
|
}
|
|
}
|
|
|
|
func LoadMessage(pSett ISettings, pData interface{}) (IMessage, error) {
|
|
var msgBytes []byte
|
|
|
|
const (
|
|
cipherSaltIndex = cSaltSize
|
|
authSaltIndex = cipherSaltIndex + cSaltSize
|
|
)
|
|
|
|
const (
|
|
proofIndex = encoding.CSizeUint64
|
|
pldLenIndex = proofIndex + encoding.CSizeUint64
|
|
hashIndex = pldLenIndex + hashing.CSHA256Size
|
|
)
|
|
|
|
switch x := pData.(type) {
|
|
case []byte:
|
|
msgBytes = x
|
|
case string:
|
|
msgBytes = encoding.HexDecode(x)
|
|
default:
|
|
return nil, ErrUnknownType
|
|
}
|
|
|
|
if len(msgBytes) < CMessageHeadSize {
|
|
return nil, ErrInvalidHeaderSize
|
|
}
|
|
|
|
cipherSaltBytes := msgBytes[:cipherSaltIndex]
|
|
authSaltBytes := msgBytes[cipherSaltIndex:authSaltIndex]
|
|
encPPHPBytes := msgBytes[authSaltIndex:]
|
|
|
|
cipher := getCipher(pSett.GetNetworkKey(), cipherSaltBytes)
|
|
pphpBytes := cipher.DecryptBytes(encPPHPBytes)
|
|
|
|
proofArr := [encoding.CSizeUint64]byte{}
|
|
copy(proofArr[:], pphpBytes[:proofIndex])
|
|
proof := encoding.BytesToUint64(proofArr)
|
|
|
|
payloadSizeArr := [encoding.CSizeUint64]byte{}
|
|
copy(payloadSizeArr[:], pphpBytes[proofIndex:pldLenIndex])
|
|
payloadLength := encoding.BytesToUint64(payloadSizeArr)
|
|
|
|
hash := pphpBytes[pldLenIndex:hashIndex]
|
|
puzzle := puzzle.NewPoWPuzzle(pSett.GetWorkSizeBits())
|
|
if !puzzle.VerifyBytes(hash, proof) {
|
|
return nil, ErrInvalidProofOfWork
|
|
}
|
|
|
|
payloadVoidBytes := pphpBytes[hashIndex:]
|
|
if payloadLength > uint64(len(payloadVoidBytes)) {
|
|
return nil, ErrInvalidPayloadSize
|
|
}
|
|
|
|
newHash := getAuthHash(pSett.GetNetworkKey(), authSaltBytes, payloadVoidBytes)
|
|
if !bytes.Equal(hash, newHash) {
|
|
return nil, ErrInvalidAuthHash
|
|
}
|
|
|
|
payloadBytes := payloadVoidBytes[:payloadLength]
|
|
payload := payload.LoadPayload(payloadBytes)
|
|
if payload == nil {
|
|
return nil, ErrDecodePayload
|
|
}
|
|
|
|
return &sMessage{
|
|
fSalt: [2][]byte{
|
|
cipherSaltBytes,
|
|
authSaltBytes,
|
|
},
|
|
fEncPPHP: encPPHPBytes,
|
|
fHash: hash,
|
|
fVoid: payloadVoidBytes[payloadLength:],
|
|
fProof: proof,
|
|
fPayload: payload,
|
|
}, nil
|
|
}
|
|
|
|
func (p *sMessage) GetProof() uint64 {
|
|
return p.fProof
|
|
}
|
|
|
|
func (p *sMessage) GetSalt() [2][]byte {
|
|
return p.fSalt
|
|
}
|
|
|
|
func (p *sMessage) GetHash() []byte {
|
|
return p.fHash
|
|
}
|
|
|
|
func (p *sMessage) GetVoid() []byte {
|
|
return p.fVoid
|
|
}
|
|
|
|
func (p *sMessage) GetPayload() payload.IPayload {
|
|
return p.fPayload
|
|
}
|
|
|
|
func (p *sMessage) ToBytes() []byte {
|
|
return bytes.Join(
|
|
[][]byte{
|
|
p.fSalt[0],
|
|
p.fSalt[1],
|
|
p.fEncPPHP,
|
|
},
|
|
[]byte{},
|
|
)
|
|
}
|
|
|
|
func (p *sMessage) ToString() string {
|
|
return encoding.HexEncode(p.ToBytes())
|
|
}
|
|
|
|
func getAuthHash(networkKey string, pAuthSalt, pBytes []byte) []byte {
|
|
authKey := keybuilder.NewKeyBuilder(1, []byte(pAuthSalt)).Build(networkKey)
|
|
return hashing.NewHMACSHA256Hasher(authKey, pBytes).ToBytes()
|
|
}
|
|
|
|
func getCipher(networkKey string, pCipherSalt []byte) symmetric.ICipher {
|
|
cipherKey := keybuilder.NewKeyBuilder(1, []byte(pCipherSalt)).Build(networkKey)
|
|
return symmetric.NewAESCipher(cipherKey)
|
|
}
|