go-peer/pkg/crypto/scheme/layer1/message.go
2026-06-01 21:36:46 +07:00

136 lines
3.0 KiB
Go

package layer1
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/symmetric"
"github.com/number571/go-peer/pkg/encoding"
)
const (
// IV + Proof + HMAC
// 16 + 8 + 48 = 72 additional bytes to origin message
CMessageHeadSize = 0 +
1*symmetric.CCipherBlockSize +
1*encoding.CSizeUint64 +
1*hashing.CHasherSize
)
const (
cProofIndex = encoding.CSizeUint64
cHashIndex = cProofIndex + hashing.CHasherSize
)
var (
_ IMessage = &sMessage{}
)
type sMessage struct {
fProof uint64 // P(HM)
fEncd []byte // E( K, P(HM) || HM || M )
fHash []byte // H( M )
fHmac []byte // HM = HMAC( K, M )
fBody []byte // M
}
func NewMessage(pSett IConstructSettings, pBody []byte) IMessage {
sett := pSett.GetSettings()
hash := hashing.NewHasher(pBody).ToBytes()
keyBuilder := keybuilder.NewKeyBuilder(0, []byte{}) // the network_key must have good entropy
key := keyBuilder.Build(sett.GetNetworkKey(), symmetric.CCipherKeySize)
hmac := hashing.NewHMACHasher(key, pBody).ToBytes()
proof := puzzle.NewPoWPuzzle(sett.GetWorkSizeBits()).ProofBytes(hmac, pSett.GetParallel())
proofBytes := encoding.Uint64ToBytes(proof)
cipher := symmetric.NewCipherCFB(key)
return &sMessage{
fEncd: cipher.EncryptBytes(bytes.Join(
[][]byte{
proofBytes[:],
hmac,
pBody,
},
[]byte{},
)),
fHash: hash,
fHmac: hmac,
fProof: proof,
fBody: pBody,
}
}
func LoadMessage(pSett ISettings, pData interface{}) (IMessage, error) {
var msgBytes []byte
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
}
keyBuilder := keybuilder.NewKeyBuilder(0, []byte{}) // the network_key must have good entropy
key := keyBuilder.Build(pSett.GetNetworkKey(), symmetric.CCipherKeySize)
dBytes := symmetric.NewCipherCFB(key).DecryptBytes(msgBytes)
proofArr := [encoding.CSizeUint64]byte{}
copy(proofArr[:], dBytes[:cProofIndex])
proof := encoding.BytesToUint64(proofArr)
hmac := dBytes[cProofIndex:cHashIndex]
puzzle := puzzle.NewPoWPuzzle(pSett.GetWorkSizeBits())
if !puzzle.VerifyBytes(hmac, proof) {
return nil, ErrInvalidProofOfWork
}
body := dBytes[cHashIndex:]
newHmac := hashing.NewHMACHasher(key, body).ToBytes()
if !bytes.Equal(hmac, newHmac) {
return nil, ErrInvalidAuthHash
}
hash := hashing.NewHasher(body).ToBytes()
return &sMessage{
fEncd: msgBytes,
fHash: hash,
fHmac: hmac,
fProof: proof,
fBody: body,
}, nil
}
func (p *sMessage) GetProof() uint64 {
return p.fProof
}
func (p *sMessage) GetHash() []byte {
return p.fHash
}
func (p *sMessage) GetHmac() []byte {
return p.fHmac
}
func (p *sMessage) GetBody() []byte {
return p.fBody
}
func (p *sMessage) ToBytes() []byte {
return p.fEncd
}
func (p *sMessage) ToString() string {
return encoding.HexEncode(p.ToBytes())
}