package opentimestamps import ( "bytes" "fmt" "io" "os" ) var fileHeaderMagic = []byte( "\x00OpenTimestamps\x00\x00Proof\x00\xbf\x89\xe2\xe8\x84\xe8\x92\x94", ) const ( minFileDigestLength = 20 maxFileDigestLength = 32 fileMajorVersion = 1 ) type DetachedTimestamp struct { HashOp cryptOp FileHash []byte Timestamp *Timestamp } func (d *DetachedTimestamp) Dump() string { w := &bytes.Buffer{} fmt.Fprintf( w, "File %s hash: %x\n", d.HashOp.name, d.Timestamp.Message, ) fmt.Fprint(w, d.Timestamp.Dump()) return w.String() } func (d *DetachedTimestamp) encode(ctx *serializationContext) error { if err := ctx.writeBytes(fileHeaderMagic); err != nil { return err } if err := ctx.writeVarUint(fileMajorVersion); err != nil { return err } if err := d.HashOp.encode(ctx); err != nil { return err } if err := ctx.writeBytes(d.FileHash); err != nil { return err } return d.Timestamp.encode(ctx) } func (d *DetachedTimestamp) WriteToStream(w io.Writer) error { return d.encode(&serializationContext{w}) } func NewDetachedTimestamp( hashOp cryptOp, fileHash []byte, ts *Timestamp, ) (*DetachedTimestamp, error) { if len(fileHash) != hashOp.digestLength { return nil, fmt.Errorf( "op %v expects %d byte digest, got %d", hashOp, hashOp.digestLength, len(fileHash), ) } return &DetachedTimestamp{hashOp, fileHash, ts}, nil } func NewDetachedTimestampFromReader(r io.Reader) (*DetachedTimestamp, error) { ctx := newDeserializationContext(r) if err := ctx.assertMagic([]byte(fileHeaderMagic)); err != nil { return nil, err } major, err := ctx.readVarUint() if err != nil { return nil, err } if major != uint64(fileMajorVersion) { return nil, fmt.Errorf("unexpected major version %d", major) } fileHashOp, err := parseCryptOp(ctx) if err != nil { return nil, err } fileHash, err := ctx.readBytes(fileHashOp.digestLength) if err != nil { return nil, err } ts, err := newTimestampFromContext(ctx, fileHash) if err != nil { return nil, err } return &DetachedTimestamp{*fileHashOp, fileHash, ts}, nil } func NewDetachedTimestampFromPath(p string) (*DetachedTimestamp, error) { f, err := os.Open(p) if err != nil { return nil, err } return NewDetachedTimestampFromReader(f) }