package mfer import ( "bytes" "crypto/sha256" "errors" "fmt" "time" "github.com/klauspost/compress/zstd" "github.com/multiformats/go-multihash" "google.golang.org/protobuf/proto" ) // MAGIC is the file format magic bytes prefix (rot13 of "MANIFEST"). const MAGIC string = "ZNAVSRFG" func newTimestampFromTime(t time.Time) *Timestamp { out := &Timestamp{ Seconds: t.Unix(), Nanos: int32(t.UnixNano() - (t.Unix() * 1000000000)), } return out } func (m *manifest) generate() error { if m.pbInner == nil { return errors.New("internal error: pbInner not set") } if m.pbOuter == nil { e := m.generateOuter() if e != nil { return e } } dat, err := proto.MarshalOptions{Deterministic: true}.Marshal(m.pbOuter) if err != nil { return err } m.output = bytes.NewBuffer([]byte(MAGIC)) _, err = m.output.Write(dat) if err != nil { return err } return nil } func (m *manifest) generateOuter() error { if m.pbInner == nil { return errors.New("internal error") } 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) idc := new(bytes.Buffer) zw, err := zstd.NewWriter(idc, zstd.WithEncoderLevel(zstd.SpeedBestCompression)) if err != nil { return err } _, err = zw.Write(innerData) if err != nil { return err } _ = zw.Close() o := &MFFileOuter{ InnerMessage: idc.Bytes(), Size: int64(len(innerData)), Sha256: sha256Hash, 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 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) if err != nil { return fmt.Errorf("failed to sign manifest: %w", err) } o.Signature = sig fingerprint, err := gpgGetKeyFingerprint(m.signingOptions.KeyID) if err != nil { return fmt.Errorf("failed to get key fingerprint: %w", err) } o.Signer = fingerprint pubKey, err := gpgExportPublicKey(m.signingOptions.KeyID) if err != nil { return fmt.Errorf("failed to export public key: %w", err) } o.SigningPubKey = pubKey } m.pbOuter = o return nil }