You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
4.2 KiB
127 lines
4.2 KiB
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
|
|
}
|