package capsule_test import ( "bytes" "context" "encoding/hex" "errors" "fmt" "io" "strings" "time" datekeys "g.activething.com/go/DateKeys" "g.activething.com/go/DateKeys/accesskey" "g.activething.com/go/DateKeys/capsule" "g.activething.com/go/DateKeys/profile" "g.activething.com/go/DateKeys/provider" ) // The published Quicknet signature of round 1000. In real use the release // comes from drand.New(), which verifies it the same way. var round1000, _ = hex.DecodeString("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39") // memorySink is a capsule.Sink that keeps the files in memory. A Sink that // writes to disk, as the datekeys CLI does, writes them to a temporary // folder and moves it into place in Commit. type memorySink struct{ files []*bytes.Buffer } func (m *memorySink) Begin(h *capsule.Head) error { m.files = make([]*bytes.Buffer, len(h.Files)) return nil } func (m *memorySink) Create(i int) (io.WriteCloser, error) { m.files[i] = new(bytes.Buffer) return bufferCloser{m.files[i]}, nil } func (m *memorySink) Commit() error { return nil } func (m *memorySink) Abort() { m.files = nil } type bufferCloser struct{ io.Writer } func (bufferCloser) Close() error { return nil } // source is a capsule.Source of a file held in memory. func source(path, content string) capsule.Source { return capsule.Source{Path: path, Size: int64(len(content)), Open: func() (io.ReadCloser, error) { return io.NopCloser(strings.NewReader(content)), nil }} } func Example() { reg, err := profile.Default() if err != nil { panic(err) } p := profile.Quicknet() // A clock stopped at the Quicknet genesis makes round 1000 // (2023-08-23T15:59:24Z) "the future", so the example runs offline. genesis := func() time.Time { return time.Unix(p.GenesisTime, 0) } var dkc bytes.Buffer res, err := capsule.EncryptFiles(&dkc, []capsule.Source{source("carta.txt", "hello from the past")}, capsule.EncryptOptions{ Profile: p, UnlockAt: time.Date(2023, 8, 23, 15, 59, 24, 0, time.UTC), Policy: capsule.TimeAndKey, NewPortableKey: true, Comment: "Para abrir dentro de un rato.", Now: genesis, }) if err != nil { panic(err) } var dkk bytes.Buffer if err := accesskey.Encode(&dkk, res.PortableKey); err != nil { panic(err) } fmt.Println("round", res.DateKey.Round, "unlocks at", res.UnlockAt.Format(time.RFC3339)) // Before the round: no request is made. key, _ := accesskey.Decode(&dkk) src := provider.ReleaseSourceFunc(func(_ context.Context, _ *profile.Profile, c provider.Condition) (provider.Release, error) { return provider.Release{Round: c.Round, Signature: round1000}, nil }) files := &memorySink{} opts := capsule.OpenOptions{Registry: reg, Source: src, AccessKey: key, Now: genesis, Sink: files} _, err = capsule.Open(context.Background(), nil, bytes.NewReader(dkc.Bytes()), opts) fmt.Println("too early:", errors.Is(err, datekeys.ErrReleaseUnavailable)) // After the round: the release is verified locally and the capsule opens. // The verdicts of the security area come before the comment. opts.Now = time.Now opened, err := capsule.Open(context.Background(), nil, bytes.NewReader(dkc.Bytes()), opts) if err != nil { panic(err) } for _, line := range opened.Verdicts.Lines() { fmt.Println(line) } fmt.Println(opened.Head.Comment) fmt.Println(opened.Head.Files[0].Path+":", files.files[0]) // Output: // round 1000 unlocks at 2023-08-23T15:59:24Z // too early: true // Sin firma de autor. // Para abrir dentro de un rato. // carta.txt: hello from the past } func ExampleInspect() { reg, _ := profile.Default() p := profile.Quicknet() var dkc bytes.Buffer _, _ = capsule.EncryptFiles(&dkc, []capsule.Source{source("x.txt", "x")}, capsule.EncryptOptions{ Profile: p, UnlockAt: time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC), Now: func() time.Time { return time.Date(2026, 9, 25, 0, 0, 0, 0, time.UTC) }, }) in, err := capsule.Inspect(bytes.NewReader(dkc.Bytes()), capsule.InspectOptions{Registry: reg}) if err != nil { panic(err) } fmt.Println(in.Header.Policy, in.Header.DateKey.Round, in.UnlockAt.Format(time.RFC3339), len(in.Checks), "checks passed") // Output: time_only 66884212 2030-01-01T00:00:00Z 8 checks passed }