Add UUID to manifest and verify integrity before decompression
- Add UUID field to both inner and outer manifest messages - Generate random v4 UUID when creating manifest - Hash compressed data (not uncompressed) for integrity check - Verify hash before decompression to prevent malicious payloads - Validate UUIDs are proper format and match between inner/outer - Sign string format: MAGIC-UUID-MULTIHASH
This commit is contained in:
@@ -3,10 +3,12 @@ package mfer
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/multiformats/go-multihash"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -49,17 +51,17 @@ func (m *manifest) generateOuter() error {
|
||||
if m.pbInner == nil {
|
||||
return errors.New("internal error")
|
||||
}
|
||||
|
||||
// Generate UUID and set on inner message
|
||||
manifestUUID := uuid.New()
|
||||
m.pbInner.Uuid = manifestUUID[:]
|
||||
|
||||
innerData, err := proto.MarshalOptions{Deterministic: true}.Marshal(m.pbInner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := h.Write(innerData); err != nil {
|
||||
return err
|
||||
}
|
||||
sha256Hash := h.Sum(nil)
|
||||
|
||||
// Compress the inner data
|
||||
idc := new(bytes.Buffer)
|
||||
zw, err := zstd.NewWriter(idc, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
|
||||
if err != nil {
|
||||
@@ -69,26 +71,40 @@ func (m *manifest) generateOuter() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = zw.Close()
|
||||
|
||||
compressedData := idc.Bytes()
|
||||
|
||||
// Hash the compressed data for integrity verification before decompression
|
||||
h := sha256.New()
|
||||
if _, err := h.Write(compressedData); err != nil {
|
||||
return err
|
||||
}
|
||||
sha256Hash := h.Sum(nil)
|
||||
|
||||
o := &MFFileOuter{
|
||||
InnerMessage: idc.Bytes(),
|
||||
InnerMessage: compressedData,
|
||||
Size: int64(len(innerData)),
|
||||
Sha256: sha256Hash,
|
||||
Uuid: manifestUUID[:],
|
||||
Version: MFFileOuter_VERSION_ONE,
|
||||
CompressionType: MFFileOuter_COMPRESSION_ZSTD,
|
||||
}
|
||||
|
||||
// Sign the manifest if signing options are provided
|
||||
if m.signingOptions != nil && m.signingOptions.KeyID != "" {
|
||||
// Encode hash as multihash for signing
|
||||
// Encode hash as multihash
|
||||
mh, err := multihash.Encode(sha256Hash, multihash.SHA2_256)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode multihash: %w", err)
|
||||
}
|
||||
|
||||
sig, err := gpgSign(mh, m.signingOptions.KeyID)
|
||||
// Build signature string: MAGIC-UUID-MULTIHASH
|
||||
uuidStr := hex.EncodeToString(manifestUUID[:])
|
||||
mhStr := hex.EncodeToString(mh)
|
||||
sigString := fmt.Sprintf("%s-%s-%s", MAGIC, uuidStr, mhStr)
|
||||
|
||||
sig, err := gpgSign([]byte(sigString), m.signingOptions.KeyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to sign manifest: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user