Rewrite vault in Rust (format-compatible with 0.5) #1

Merged
andydixon merged 19 commits from rust-port into master 2026-10-08 18:38:32 +02:00
60 changed files with 4843 additions and 0 deletions
Showing only changes of commit 69f4b90777 - Show all commits
+42
View File
@@ -0,0 +1,42 @@
# Go oracle (test-only)
`oracle/go` is a frozen copy of the Go implementation of vault, taken from
commit `4c1507c` ("Add unlock --idle, and packaging/repo.sh for
repo.dixon.cx"), the last Go release on `master`. It is kept as the
reference that the Rust implementation is checked against. It is **not**
part of the shipped product: release builds, packages and the `vault`
binary contain no Go code and need no Go toolchain.
Provenance:
- Every file under `oracle/go` is byte-identical to the same path at the
repository root at `4c1507c`, except the three added test files below.
- `oracle_vectors_test.go`, `internal/pqcrypt/oracle_vectors_test.go` and
`internal/stream/oracle_vectors_test.go` are test-only generators. They
call the frozen code and write what it computes; they skip themselves
unless `VAULT_ORACLE_OUT` is set, and add nothing to the oracle binary.
- Dependencies are pinned by `go.mod`/`go.sum`: CIRCL v1.6.4,
golang.org/x/crypto v0.53.0, x/sys v0.46.0, x/term v0.44.0. The module
needs Go 1.26.6 or newer.
Check the copy is still faithful:
```sh
git diff --stat 4c1507c HEAD -M -- oracle/go # renames only, plus the generators
```
Use:
```sh
oracle/gen-vectors.sh # rewrite tests/vectors from the oracle
(cd oracle/go && go build -o ../../target/vault-go .) # build the oracle CLI
(cd oracle/go && go test ./...) # its own test-suite
```
`cargo test` builds the oracle binary itself for the interoperability tests
when `go` is on `PATH` (or uses `$VAULT_GO_ORACLE`), and reports them as
skipped otherwise.
All passphrases, seeds and plaintexts in the vectors are synthetic and
public (`oracle-real passphrase`, `oracle-decoy passphrase`, fixed byte
patterns). Never commit real keys or real vault contents.
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Regenerate the Go-oracle vectors and fixtures in tests/vectors.
#
# Everything is computed by the frozen Go implementation in oracle/go (see
# oracle/README.md); nothing comes from the Rust code under test. Key files
# and ciphertexts use fresh randomness, so regenerating changes them; the
# deterministic vectors (*.json fields derived from fixed seeds) do not.
set -euo pipefail
cd "$(dirname "$0")/.."
out=$PWD/tests/vectors
mkdir -p "$out"
(cd oracle/go && VAULT_ORACLE_OUT=$out go test -count=1 -run '^TestOracleVectors$' ./ ./internal/pqcrypt ./internal/stream)
echo "vectors written to $out"
View File
View File
View File
View File
@@ -0,0 +1,298 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Test-only vector generator for the Rust port. It adds nothing to the
// production code: it only calls the frozen implementation and records what
// it computes. Run through oracle/gen-vectors.sh.
package pqcrypt
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"os"
"path/filepath"
"testing"
"github.com/andydixon/vault/internal/format"
"github.com/cloudflare/circl/kem"
"github.com/cloudflare/circl/kem/frodo/frodo640shake"
"github.com/cloudflare/circl/kem/mlkem/mlkem768"
"golang.org/x/crypto/hkdf"
)
type kemVector struct {
Seed string `json:"seed"`
PubSHA256 string `json:"pub_sha256"`
PrivSHA256 string `json:"priv_sha256"`
EncapSeed string `json:"encap_seed"`
CtSHA256 string `json:"ct_sha256"`
SharedSecret string `json:"ss"`
RejectFlipIdx int `json:"reject_flip_index"`
RejectSS string `json:"reject_ss"`
}
type hybridVector struct {
MasterSeed string `json:"master_seed"`
FrodoSeed string `json:"frodo_seed"`
MLKEMSeed string `json:"mlkem_seed"`
PubSHA256 string `json:"pub_sha256"`
PrivSHA256 string `json:"priv_sha256"`
PubPrefix string `json:"pub_prefix"`
}
type wrapVector struct {
MasterSeed string `json:"master_seed"`
FrodoEncap string `json:"frodo_encap_seed"`
MLKEMEncap string `json:"mlkem_encap_seed"`
FileKey string `json:"file_key"`
KEK string `json:"kek"`
Header string `json:"header_hex"` // full serialised header, MAC line included
MAC string `json:"mac"`
Nonce string `json:"nonce"`
PayloadKey string `json:"payload_key"`
StanzaArg0 string `json:"stanza_arg0_sha256"`
StanzaBody string `json:"stanza_body"`
}
type headerCase struct {
Input string `json:"input_hex"`
Accept bool `json:"accept"`
Rest string `json:"rest_hex,omitempty"` // payload bytes after the header
}
func sum(b []byte) string { s := sha256.Sum256(b); return hex.EncodeToString(s[:]) }
func pattern(n int, salt byte) []byte {
b := make([]byte, n)
for i := range b {
b[i] = byte(i*7) ^ salt
}
return b
}
func kemVectors(t *testing.T, s kem.Scheme, n int) []kemVector {
var out []kemVector
for i := range n {
seed := pattern(s.SeedSize(), byte(i*31))
pk, sk := s.DeriveKeyPair(seed)
pkb, _ := pk.MarshalBinary()
skb, _ := sk.MarshalBinary()
es := pattern(s.EncapsulationSeedSize(), byte(i*17+5))
ct, ss, err := s.EncapsulateDeterministically(pk, es)
if err != nil {
t.Fatal(err)
}
ss2, err := s.Decapsulate(sk, ct)
if err != nil || !bytes.Equal(ss, ss2) {
t.Fatal("decapsulation mismatch")
}
flip := (i * 997) % len(ct)
bad := append([]byte(nil), ct...)
bad[flip] ^= 0x01
rss, err := s.Decapsulate(sk, bad)
if err != nil {
t.Fatal(err)
}
out = append(out, kemVector{
Seed: hex.EncodeToString(seed), PubSHA256: sum(pkb), PrivSHA256: sum(skb),
EncapSeed: hex.EncodeToString(es), CtSHA256: sum(ct), SharedSecret: hex.EncodeToString(ss),
RejectFlipIdx: flip, RejectSS: hex.EncodeToString(rss),
})
}
return out
}
func TestOracleVectors(t *testing.T) {
dir := os.Getenv("VAULT_ORACLE_OUT")
if dir == "" {
t.Skip("set VAULT_ORACLE_OUT to write vectors")
}
suite := frodoMLKEM768{}
frodo, ml := frodo640shake.Scheme(), mlkem768.Scheme()
var hybrids []hybridVector
for i := range 6 {
master := pattern(32, byte(i*53+1))
if i == 5 {
master = bytes.Repeat([]byte{0xff}, 32)
}
r := hkdf.New(sha256.New, master, nil, []byte("fury/v1 seed-expand "+suite.tag()))
fs := make([]byte, frodo.SeedSize())
ms := make([]byte, ml.SeedSize())
io.ReadFull(r, fs)
io.ReadFull(r, ms)
pub, priv, err := suite.deriveKeypair(master)
if err != nil {
t.Fatal(err)
}
hybrids = append(hybrids, hybridVector{
MasterSeed: hex.EncodeToString(master), FrodoSeed: hex.EncodeToString(fs), MLKEMSeed: hex.EncodeToString(ms),
PubSHA256: sum(pub), PrivSHA256: sum(priv), PubPrefix: hex.EncodeToString(pub[:64]),
})
}
// A wrap with controlled encapsulation randomness, built from the real
// deriveKEK, aeadEncrypt, header marshalling, headerMAC and streamKey.
var wraps []wrapVector
for i := range 3 {
master := pattern(32, byte(i*53+1))
id, err := NewPQIdentityFromSeed(suite.id(), master)
if err != nil {
t.Fatal(err)
}
pub := id.Recipient().Bytes()
fpk, _ := frodo.UnmarshalBinaryPublicKey(pub[:frodo.PublicKeySize()])
mpk, _ := ml.UnmarshalBinaryPublicKey(pub[frodo.PublicKeySize():])
fe, me := pattern(16, byte(i+100)), pattern(32, byte(i+150))
ct1, ss1, _ := frodo.EncapsulateDeterministically(fpk, fe)
ct2, ss2, _ := ml.EncapsulateDeterministically(mpk, me)
kek := suite.deriveKEK(ss1, ss2, ct1, ct2)
fileKey := pattern(32, byte(i+200))
body, _ := aeadEncrypt(kek, fileKey)
st := &format.Stanza{Type: suite.tag(), Args: []string{format.EncodeToString(ct1), format.EncodeToString(ct2)}, Body: body}
hdr := &format.Header{Recipients: []*format.Stanza{st}}
mac, err := headerMAC(fileKey, hdr)
if err != nil {
t.Fatal(err)
}
hdr.MAC = mac
var hb bytes.Buffer
hdr.Marshal(&hb)
// The real identity must open what was built.
got, err := id.Unwrap([]*Stanza{(*Stanza)(st)})
if err != nil || !bytes.Equal(got, fileKey) {
t.Fatal("identity did not unwrap the vector")
}
nonce := pattern(16, byte(i+250))
wraps = append(wraps, wrapVector{
MasterSeed: hex.EncodeToString(master), FrodoEncap: hex.EncodeToString(fe), MLKEMEncap: hex.EncodeToString(me),
FileKey: hex.EncodeToString(fileKey), KEK: hex.EncodeToString(kek), Header: hex.EncodeToString(hb.Bytes()),
MAC: hex.EncodeToString(mac), Nonce: hex.EncodeToString(nonce), PayloadKey: hex.EncodeToString(streamKey(fileKey, nonce)),
StanzaArg0: sum([]byte(st.Args[0])), StanzaBody: hex.EncodeToString(body),
})
}
// Whole files from the real Encrypt, with fresh randomness, for the
// first hybrid identity. Plaintexts are pattern(size, 0x5a).
id, _ := NewPQIdentityFromSeed(suite.id(), pattern(32, 1))
fdir := filepath.Join(dir, "fury")
os.MkdirAll(fdir, 0o755)
for _, size := range []int{0, 1, 65535, 65536, 65537, 131072, 200000} {
var buf bytes.Buffer
w, err := Encrypt(&buf, id.Recipient())
if err != nil {
t.Fatal(err)
}
w.Write(pattern(size, 0x5a))
if err := w.Close(); err != nil {
t.Fatal(err)
}
os.WriteFile(filepath.Join(fdir, "pattern-"+itoa(size)+".fury"), buf.Bytes(), 0o644)
}
// Header parser verdicts on valid and malformed headers.
var cases []headerCase
valid, _ := hex.DecodeString(wraps[0].Header)
add := func(b []byte) {
h, payload, err := format.Parse(bytes.NewReader(b))
c := headerCase{Input: hex.EncodeToString(b), Accept: err == nil}
if err == nil {
rest, _ := io.ReadAll(payload)
c.Rest = hex.EncodeToString(rest)
var again bytes.Buffer
h.Marshal(&again)
}
cases = append(cases, c)
}
for _, m := range headerMutations(valid) {
add(m)
}
write(t, filepath.Join(dir, "crypto.json"), map[string]any{
"provenance": "oracle/go at 4c1507c, internal/pqcrypt/oracle_vectors_test.go",
"frodo640shake": kemVectors(t, frodo, 8),
"mlkem768": kemVectors(t, ml, 8),
"hybrid": hybrids,
"wrap": wraps,
"header_cases": cases,
})
}
func itoa(n int) string { return string(appendInt(nil, n)) }
func appendInt(b []byte, n int) []byte {
if n >= 10 {
b = appendInt(b, n/10)
}
return append(b, byte('0'+n%10))
}
// headerMutations returns valid headers plus a range of malformed ones.
func headerMutations(valid []byte) [][]byte {
s := string(valid)
introEnd := bytes.IndexByte(valid, '\n') + 1
footer := bytes.LastIndex(valid, []byte("\n---")) + 1
lines := bytes.SplitAfter(valid, []byte("\n"))
join := func(ls [][]byte) []byte { return bytes.Join(ls, nil) }
var out [][]byte
push := func(b []byte) { out = append(out, append([]byte(nil), b...)) }
push(valid)
push(append(append([]byte(nil), valid...), []byte("payload\x00\x01")...))
push([]byte(""))
push([]byte("fury-encryption.org/v1\n--- " + s[len(s)-44:])) // no stanzas
push([]byte("fury-encryption.org/v2\n" + s[introEnd:])) // wrong intro
push([]byte("fury-encryption.org/v1" + s[introEnd:])) // intro without newline
push(valid[:footer]) // no footer
push(valid[:len(valid)-1]) // footer without newline
push(append(append([]byte(nil), valid[:len(valid)-1]...), '=', '\n')) // padded MAC
push([]byte(s[:footer] + "--- " + s[footer+4:])) // double space
push([]byte(s[:footer] + "---x " + s[footer+4:])) // bad footer prefix
push([]byte(s[:footer] + "--- " + s[footer+4:len(s)-2] + "\n")) // short MAC
push([]byte(s[:footer] + "--- " + s[footer+4:len(s)-1] + " x\n"))
// Stanza line edits.
stanza := lines[1]
for _, edit := range []string{"-> \n", "->\n", "-> x\n", "-> x\x7f\n", "-> x é\n", "-x a\n", "-> x y\n", "-> x\r\n"} {
ls := append([][]byte(nil), lines...)
ls[1] = []byte(edit)
push(join(ls))
_ = stanza
}
// Body edits: the body is the two lines after the stanza line.
body := 2
for _, edit := range []string{"\n", "AAAA\n", "AAAA=\n", "A\n", "AB\n", "AAB\n", "AA==\n", " AAAA\n", "AAAA\r\n",
string(bytes.Repeat([]byte("A"), 64)) + "\n", string(bytes.Repeat([]byte("A"), 68)) + "\n", string(bytes.Repeat([]byte("A"), 65)) + "\n"} {
ls := append([][]byte(nil), lines...)
ls[body] = []byte(edit)
push(join(ls))
}
{
// Drop the empty line that ends a 48-byte body: the stanza then runs
// into the footer.
ls := append([][]byte(nil), lines...)
ls = append(ls[:3], ls[4:]...)
push(join(ls))
}
{
// A second, foreign stanza ahead of the real one.
ls := append([][]byte(nil), lines[:1]...)
ls = append(ls, []byte("-> other-suite arg\n"), []byte("AAAA\n"))
ls = append(ls, lines[1:]...)
push(join(ls))
}
// The 1 MiB stanza and 16 MiB header limits are exercised by the Rust
// tests directly; vectors that size would bloat the repository.
return out
}
func write(t *testing.T, path string, v any) {
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, append(b, '\n'), 0o644); err != nil {
t.Fatal(err)
}
}
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Test-only vector generator for the Rust port; see oracle/gen-vectors.sh.
package stream
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"os"
"path/filepath"
"testing"
)
func TestOracleVectors(t *testing.T) {
dir := os.Getenv("VAULT_ORACLE_OUT")
if dir == "" {
t.Skip("set VAULT_ORACLE_OUT to write vectors")
}
key := make([]byte, 32)
for i := range key {
key[i] = byte(i + 1)
}
type vec struct {
Size int `json:"size"`
CtLen int `json:"ct_len"`
CtSHA256 string `json:"ct_sha256"`
CtHex string `json:"ct_hex,omitempty"`
}
var vs []vec
for _, size := range []int{0, 1, 15, 16, 17, ChunkSize - 1, ChunkSize, ChunkSize + 1, 2 * ChunkSize, 2*ChunkSize + 1, 3*ChunkSize - 16, 300000} {
pt := make([]byte, size)
for i := range pt {
pt[i] = byte(i*13 + 7)
}
var buf bytes.Buffer
w, _ := NewEncryptWriter(key, &buf)
w.Write(pt)
w.Close()
// The reader-side encryptor must agree.
r, _ := NewEncryptReader(key, bytes.NewReader(pt))
alt, _ := io.ReadAll(r)
if !bytes.Equal(alt, buf.Bytes()) {
t.Fatal("EncryptReader disagrees with EncryptWriter")
}
s := sha256.Sum256(buf.Bytes())
v := vec{Size: size, CtLen: buf.Len(), CtSHA256: hex.EncodeToString(s[:])}
if size <= 17 {
v.CtHex = hex.EncodeToString(buf.Bytes())
}
vs = append(vs, v)
}
b, _ := json.MarshalIndent(map[string]any{
"provenance": "oracle/go at 4c1507c, internal/stream/oracle_vectors_test.go",
"key": hex.EncodeToString(key),
"plaintext": "byte(i*13+7)",
"vectors": vs,
}, "", " ")
if err := os.WriteFile(filepath.Join(dir, "stream.json"), append(b, '\n'), 0o644); err != nil {
t.Fatal(err)
}
}
View File
View File
View File
View File
+208
View File
@@ -0,0 +1,208 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Test-only vector and fixture generator for the Rust port. It calls the
// frozen implementation and records what it computes; it changes nothing in
// the vault binary. Run through oracle/gen-vectors.sh.
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path"
"path/filepath"
"testing"
"time"
"github.com/andydixon/vault/internal/pqcrypt"
"golang.org/x/crypto/argon2"
)
// Synthetic, public test passphrases. Never use them for anything real.
const (
oracleReal = "oracle-real passphrase"
oracleDecoy = "oracle-decoy passphrase"
)
func TestOracleVectors(t *testing.T) {
dir := os.Getenv("VAULT_ORACLE_OUT")
if dir == "" {
t.Skip("set VAULT_ORACLE_OUT to write vectors")
}
out := map[string]any{"provenance": "oracle/go at 4c1507c, oracle_vectors_test.go"}
// Key files.
keys := map[string]any{}
for _, k := range []struct{ name, real, decoy string }{
{"key-with-decoy.pem", oracleReal, oracleDecoy},
{"key-no-decoy.pem", oracleReal, ""},
} {
p := filepath.Join(dir, k.name)
os.Remove(p)
if err := writeKeyFile(p, k.real, k.decoy); err != nil {
t.Fatal(err)
}
entry := map[string]any{}
for label, pass := range map[string]string{"real": k.real, "decoy": k.decoy} {
if pass == "" {
continue
}
id, err := unlockKeyFile(p, pass)
if err != nil {
t.Fatal(err)
}
entry[label] = map[string]any{"passphrase": pass, "suite": id.SuiteID(), "seed": hex.EncodeToString(id.Seed())}
}
keys[k.name] = entry
}
out["keys"] = keys
var argon []map[string]string
for i, pass := range []string{"", "a", "correct horse", "pässwörd ü", "x\x00y"} {
salt := make([]byte, 16)
for j := range salt {
salt[j] = byte(i*16 + j)
}
k := argon2.IDKey([]byte(pass), salt, argonTime, argonMemory, argonThreads, 32)
argon = append(argon, map[string]string{"passphrase_hex": hex.EncodeToString([]byte(pass)), "salt": hex.EncodeToString(salt), "key": hex.EncodeToString(k)})
}
out["argon2id"] = argon
out["key_header_hex"] = hex.EncodeToString(keyHeader())
// Object IDs and sizes.
seed := "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
var ids []string
for i := range int64(4) {
ids = append(ids, objectID(seed, i))
}
out["object_ids"] = map[string]any{"seed": seed, "ids": ids, "big_index": objectID(seed, 1<<40), "big_index_value": int64(1 << 40)}
padded := map[string]int64{}
counts := map[string]int64{}
for _, n := range []int64{0, 1, 16, 65535, 65536, 65537, 1 << 20, chunkSize - 1, chunkSize, chunkData - 1, chunkData, chunkData + 1, 2 * chunkData, fileLimit} {
padded[fmt.Sprint(n)] = paddedSize(n)
counts[fmt.Sprint(n)] = objectCount(n)
}
out["padded_size"] = padded
out["object_count"] = counts
names := map[string]bool{}
for _, n := range []string{"", "a", "a/b", "/a", "a/", "a//b", "./a", "a/./b", "../a", "a/..", "..", ".", "a/../b", "a\\b", "a\x00b",
"a\nb", "a\x7fb", "a\u0085b", "ü/ñ", "\xff", "a b", " a", "...", "a/...", "..a", "a/.b", string(make([]byte, 1024)), longName(1024), longName(1025), "a​b", "
"} {
names[hex.EncodeToString([]byte(n))] = validName(n)
}
out["valid_name_hex"] = names
sizes := map[string]any{}
for _, s := range []string{"0", "512", "2k", "500M", "2G", "1TB", "", "G", "-1", "2X", "1.5G", "2T", "+5", "1KB", "1MK", "1kb", "B", "1T", "1099511627776", "1099511627777", " 1", "0x10", "1024G", "1025G", "-0", "1bb"} {
n, err := parseSize(s)
if err != nil {
sizes[s] = nil
} else {
sizes[s] = n
}
}
out["parse_size"] = sizes
human := map[string]string{}
for _, n := range []int64{0, 1, 1023, 1024, 1025, 1536, 1101, 10239, 10240, 1048575, 1048576, 1 << 30, 1<<40 - 1, 1 << 40, 1<<62 + 12345, 1<<63 - 1, 1075, 1126, 1177, 1638, 1689} {
human[fmt.Sprint(n)] = humanSize(n)
}
out["human_size"] = human
clean := map[string]string{}
for _, p := range []string{"", ".", "..", "/", "//", "a/b/../c", "/../a", "a/./b/", "../../a", "a//b", "./", "/a/b/..", "a/b/../../..", "abc/def/ghi/../.."} {
clean[p] = path.Clean(p)
}
out["path_clean"] = clean
durs := map[string]any{}
for _, d := range []string{"5m", "1h", "90s", "1.5h", "300ms", "1h30m", "0", "-5m", "", "5", "1d", "2h45m30.5s", "1us", "1µs", "1ns", ".5s", "5.s", "+5m", "9223372036854775807ns", "9223372036854775808ns"} {
v, err := time.ParseDuration(d)
if err != nil {
durs[d] = nil
} else {
durs[d] = map[string]any{"ns": int64(v), "string": v.String()}
}
}
out["durations"] = durs
// Record JSON, exactly as written.
var recs []map[string]any
for _, r := range []record{
{Name: "plain.txt", Seed: seed, Size: 5, Mode: 0o644, Modified: time.Unix(1700000000, 123456789).UTC(), Added: time.Unix(1700000001, 0).UTC()},
{Name: "<a&b>/

'\"\\x", Seed: seed, Size: 0, Mode: 0o755, Modified: time.Unix(0, 0).UTC(), Added: time.Unix(1, 100).UTC()},
{Name: "ü/日本語", Seed: seed, Size: 1 << 40, Mode: 0o600, Modified: time.Unix(-1, 500000000).UTC(), Added: time.Unix(253402300799, 999999999).UTC()},
{Name: "tab\there", Seed: seed, Size: 1, Mode: 0, Modified: time.Unix(1234567890, 120000000).UTC(), Added: time.Unix(1234567890, 1000).UTC()},
} {
b, err := json.Marshal(&r)
if err != nil {
t.Fatal(err)
}
recs = append(recs, map[string]any{"json": string(b), "name": r.Name, "modified_unix_nano": r.Modified.UnixNano(), "added_unix_nano": r.Added.UnixNano()})
}
out["records"] = recs
writeMiniVault(t, filepath.Join(dir, "mini-vault"), filepath.Join(dir, "key-with-decoy.pem"))
b, _ := json.MarshalIndent(out, "", " ")
if err := os.WriteFile(filepath.Join(dir, "store.json"), append(b, '\n'), 0o644); err != nil {
t.Fatal(err)
}
}
func longName(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = 'a' + byte(i%26)
}
return string(b)
}
// writeMiniVault builds a small vault through the real store code, without
// init's filler (which would make the fixture megabytes): three real files,
// one decoy file and one record sealed to a throwaway key.
func writeMiniVault(t *testing.T, root, keyPath string) {
os.RemoveAll(root)
for _, d := range []string{recordsDir, objectsDir} {
for i := range 256 {
if err := os.MkdirAll(filepath.Join(root, d, fmt.Sprintf("%02x", i)), 0o700); err != nil {
t.Fatal(err)
}
}
}
os.WriteFile(filepath.Join(root, markerName), []byte(markerContent), 0o600)
src := filepath.Join(t.TempDir(), "src")
put := func(s *store, name string, body []byte, mode os.FileMode, mtime time.Time) {
os.WriteFile(src, body, 0o600)
os.Chmod(src, mode)
os.Chtimes(src, mtime, mtime)
cat, err := s.catalogue()
if err != nil {
t.Fatal(err)
}
if _, err := s.add(cat, src, name); err != nil {
t.Fatal(err)
}
}
real, _ := unlockKeyFile(keyPath, oracleReal)
decoy, _ := unlockKeyFile(keyPath, oracleDecoy)
throwaway, _ := pqcrypt.GeneratePQIdentity()
rs := &store{root: root, id: real}
boundary := make([]byte, minPadding-idSize)
for i := range boundary {
boundary[i] = byte(i * 7)
}
put(rs, "hello.txt", []byte("hello"), 0o644, time.Unix(1700000000, 123456789))
put(rs, "dir/empty", nil, 0o600, time.Unix(1600000000, 0))
put(rs, "dir/ünïcode.bin", boundary, 0o755, time.Unix(1500000000, 999999999))
put(&store{root: root, id: decoy}, "shopping.txt", []byte("milk\n"), 0o600, time.Unix(1700000000, 0))
put(&store{root: root, id: throwaway}, "foreign", []byte("not yours"), 0o600, time.Unix(1700000000, 0))
for _, d := range []string{recordsDir, objectsDir} {
shards, _ := filepath.Glob(filepath.Join(root, d, "??"))
for _, p := range append(shards, filepath.Join(root, d)) {
os.Chtimes(p, epoch, epoch)
}
}
}
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+7
View File
@@ -0,0 +1,7 @@
-----BEGIN VAULT KEY-----
VkFVTFRLRVkxAAAAAwABAAAE7NkqKMY7sbZZjHitika7SuJ2Nn8IXjsNrc05bRD3
/5n6gg09m6shM9w4JZCrZ84P9QdttuuLaTotx4TIi6ALwRpuQ1GETVN8aC2cPUlQ
eAZjKThQyzilIkqfNWBEk6O1SWHiH4RM7wvmXM2HraTzWiKrLC0hc8Fhcdwv/eDR
kM6uh6iAmk74hWC8PIWuSscS5aW7Vl2OAyKr7vISRH611vXXsSwAAcEAPcoMeKgD
YQcl9EJX
-----END VAULT KEY-----
+7
View File
@@ -0,0 +1,7 @@
-----BEGIN VAULT KEY-----
VkFVTFRLRVkxAAAAAwABAAAEgOQrPuXO8L/uhqn/gPncOQDW1UGohTv1BUEG37eX
g/wZRVl2Nptu6xbqAhL1xgLstttod4sT5iNfFxvYsZnSQLsCMjaNwO5/a8rn8fOw
QhRKx38573ADf3kA8Hv9qiykMgEAa3Ml9Qz6vlqB5gRWuK4meXSpEuxdz7XcpxIn
lOo0jYKzagOhNM40jTBkiQmJvvYJIWt48Ckl/goDk+GQRY1SWFRW0ioQF89dHK7D
XlBfBz9o
-----END VAULT KEY-----
+1
View File
@@ -0,0 +1 @@
VAULT 2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+291
View File
@@ -0,0 +1,291 @@
{
"argon2id": [
{
"key": "ad4a178e6b0786a48f9bb3443df83f76b822c6fdac15a80277483fd84551ac52",
"passphrase_hex": "",
"salt": "000102030405060708090a0b0c0d0e0f"
},
{
"key": "31d553d292080fe042ee1a2d65b3673aedaa45cc63611410a866805455452d2c",
"passphrase_hex": "61",
"salt": "101112131415161718191a1b1c1d1e1f"
},
{
"key": "537025a925815556ec88beffea074f810f5a28b6c5f5ccea0077f2819e8b3fb8",
"passphrase_hex": "636f727265637420686f727365",
"salt": "202122232425262728292a2b2c2d2e2f"
},
{
"key": "24d4e3608a444089a6f78ef8b07946879e570652b8387414895e0ad55ae68fb2",
"passphrase_hex": "70c3a4737377c3b6726420c3bc",
"salt": "303132333435363738393a3b3c3d3e3f"
},
{
"key": "bccc0063f95f2003f01532a75ae0feff25bdce4d940b904753d3b0326563a944",
"passphrase_hex": "780079",
"salt": "404142434445464748494a4b4c4d4e4f"
}
],
"durations": {
"": null,
"+5m": {
"ns": 300000000000,
"string": "5m0s"
},
"-5m": {
"ns": -300000000000,
"string": "-5m0s"
},
".5s": {
"ns": 500000000,
"string": "500ms"
},
"0": {
"ns": 0,
"string": "0s"
},
"1.5h": {
"ns": 5400000000000,
"string": "1h30m0s"
},
"1d": null,
"1h": {
"ns": 3600000000000,
"string": "1h0m0s"
},
"1h30m": {
"ns": 5400000000000,
"string": "1h30m0s"
},
"1ns": {
"ns": 1,
"string": "1ns"
},
"1us": {
"ns": 1000,
"string": "1µs"
},
"1µs": {
"ns": 1000,
"string": "1µs"
},
"2h45m30.5s": {
"ns": 9930500000000,
"string": "2h45m30.5s"
},
"300ms": {
"ns": 300000000,
"string": "300ms"
},
"5": null,
"5.s": {
"ns": 5000000000,
"string": "5s"
},
"5m": {
"ns": 300000000000,
"string": "5m0s"
},
"90s": {
"ns": 90000000000,
"string": "1m30s"
},
"9223372036854775807ns": {
"ns": 9223372036854775807,
"string": "2562047h47m16.854775807s"
},
"9223372036854775808ns": null
},
"human_size": {
"0": "0 B",
"1": "1 B",
"1023": "1023 B",
"10239": "10.0 KiB",
"1024": "1.0 KiB",
"10240": "10.0 KiB",
"1025": "1.0 KiB",
"1048575": "1024.0 KiB",
"1048576": "1.0 MiB",
"1073741824": "1.0 GiB",
"1075": "1.0 KiB",
"1099511627775": "1024.0 GiB",
"1099511627776": "1.0 TiB",
"1101": "1.1 KiB",
"1126": "1.1 KiB",
"1177": "1.1 KiB",
"1536": "1.5 KiB",
"1638": "1.6 KiB",
"1689": "1.6 KiB",
"4611686018427400249": "4.0 EiB",
"9223372036854775807": "8.0 EiB"
},
"key_header_hex": "5641554c544b455931000000030001000004",
"keys": {
"key-no-decoy.pem": {
"real": {
"passphrase": "oracle-real passphrase",
"seed": "6ba0719d2a5433702aa5cc4948ef81f0dfc74ca1a3d1bb0a7ebcf08b283c17d4",
"suite": 1
}
},
"key-with-decoy.pem": {
"decoy": {
"passphrase": "oracle-decoy passphrase",
"seed": "8123774aca365941cfbb50482315c8bdba09744d24b5f3ae9a6597578b246259",
"suite": 1
},
"real": {
"passphrase": "oracle-real passphrase",
"seed": "d11a7d21d5eb484aff4fbbfb4dd05469e719ca0338d8475bd0547fe7e42e706c",
"suite": 1
}
}
},
"object_count": {
"0": 1,
"1": 1,
"1048576": 1,
"1099511627776": 262146,
"16": 1,
"4194287": 1,
"4194288": 1,
"4194289": 2,
"4194303": 2,
"4194304": 2,
"65535": 1,
"65536": 1,
"65537": 1,
"8388576": 2
},
"object_ids": {
"big_index": "0f793077e14e9c9c44cffd32229248aa",
"big_index_value": 1099511627776,
"ids": [
"9f0cd9b94097fe4929918d2b8942b344",
"c432e059c378eef7fe2f1181a4050836",
"f92ad613cd014c7449fcc5d4ce98ad02",
"96cee9f29e43c395c608a454b854da2b"
],
"seed": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
},
"padded_size": {
"0": 65536,
"1": 65536,
"1048576": 1048576,
"1099511627776": 1099511627776,
"16": 65536,
"4194287": 4194304,
"4194288": 4194304,
"4194289": 4194304,
"4194303": 4194304,
"4194304": 4194304,
"65535": 65536,
"65536": 65536,
"65537": 131072,
"8388576": 8388608
},
"parse_size": {
"": null,
" 1": null,
"+5": 5,
"-0": 0,
"-1": null,
"0": 0,
"0x10": null,
"1.5G": null,
"1024G": 1099511627776,
"1025G": null,
"1099511627776": 1099511627776,
"1099511627777": null,
"1KB": 1024,
"1MK": 1048576,
"1T": 1099511627776,
"1TB": 1099511627776,
"1bb": null,
"1kb": 1024,
"2G": 2147483648,
"2T": null,
"2X": null,
"2k": 2048,
"500M": 524288000,
"512": 512,
"B": null,
"G": null
},
"path_clean": {
"": ".",
".": ".",
"..": "..",
"../../a": "../../a",
"./": ".",
"/": "/",
"/../a": "/a",
"//": "/",
"/a/b/..": "/a",
"a/./b/": "a/b",
"a//b": "a/b",
"a/b/../../..": "..",
"a/b/../c": "a/c",
"abc/def/ghi/../..": "abc"
},
"provenance": "oracle/go at 4c1507c, oracle_vectors_test.go",
"records": [
{
"added_unix_nano": 1700000001000000000,
"json": "{\"name\":\"plain.txt\",\"seed\":\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\",\"size\":5,\"mode\":420,\"modified\":\"2023-11-14T22:13:20.123456789Z\",\"added\":\"2023-11-14T22:13:21Z\"}",
"modified_unix_nano": 1700000000123456789,
"name": "plain.txt"
},
{
"added_unix_nano": 1000000100,
"json": "{\"name\":\"\\u003ca\\u0026b\\u003e/\\u2028\\u2029'\\\"\\\\x\",\"seed\":\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\",\"size\":0,\"mode\":493,\"modified\":\"1970-01-01T00:00:00Z\",\"added\":\"1970-01-01T00:00:01.0000001Z\"}",
"modified_unix_nano": 0,
"name": "\u003ca\u0026b\u003e/\u2028\u2029'\"\\x"
},
{
"added_unix_nano": -4852116231933722625,
"json": "{\"name\":\"ü/日本語\",\"seed\":\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\",\"size\":1099511627776,\"mode\":384,\"modified\":\"1969-12-31T23:59:59.5Z\",\"added\":\"9999-12-31T23:59:59.999999999Z\"}",
"modified_unix_nano": -500000000,
"name": "ü/日本語"
},
{
"added_unix_nano": 1234567890000001000,
"json": "{\"name\":\"tab\\there\",\"seed\":\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\",\"size\":1,\"mode\":0,\"modified\":\"2009-02-13T23:31:30.12Z\",\"added\":\"2009-02-13T23:31:30.000001Z\"}",
"modified_unix_nano": 1234567890120000000,
"name": "tab\there"
}
],
"valid_name_hex": {
"": false,
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000": false,
"2061": true,
"2e": false,
"2e2e": false,
"2e2e2e": true,
"2e2e2f61": false,
"2e2e61": true,
"2e2f61": false,
"2f61": false,
"61": true,
"610062": false,
"610a62": false,
"612062": true,
"612f": false,
"612f2e2e": false,
"612f2e2e2e": true,
"612f2e2e2f62": false,
"612f2e2f62": false,
"612f2e62": true,
"612f2f62": false,
"612f62": true,
"615c62": false,
"6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a": true,
"6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b6c6d6e6f707172737475767778797a6162636465666768696a6b": false,
"617f62": false,
"61c28562": false,
"61e2808b62": true,
"c3bc2fc3b1": true,
"e280a8": true,
"ff": false
}
}
+72
View File
@@ -0,0 +1,72 @@
{
"key": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
"plaintext": "byte(i*13+7)",
"provenance": "oracle/go at 4c1507c, internal/stream/oracle_vectors_test.go",
"vectors": [
{
"size": 0,
"ct_len": 16,
"ct_sha256": "b5e6c4d4c2d8e2448c7b13550cb447361c6319f8303eb2189e3c00109946633e",
"ct_hex": "ebc87bb498d2cf767b620485d8e74873"
},
{
"size": 1,
"ct_len": 17,
"ct_sha256": "4b431b2d3c656dd5565f045f3afaa216a29bd624d84b14a6b15ad7fcdf0bcc3c",
"ct_hex": "13e2ae1e27b20a055d353c18584d29dfec"
},
{
"size": 15,
"ct_len": 31,
"ct_sha256": "a226ce0a65bacca756d4620c4a8103df0009486b7db9a36c577ac305c03f23bb",
"ct_hex": "13485b85b3b4d036ed77edd5eb120ae816391a9b23c88452779018a527ffd0"
},
{
"size": 16,
"ct_len": 32,
"ct_sha256": "fa29f52ecc1bce80108d7fc89f1d55ec231ab3eb9308d0ded0bb525e3ec6addd",
"ct_hex": "13485b85b3b4d036ed77edd5eb120afe1d7171433f4aa6d81d9f1a1c43f1563f"
},
{
"size": 17,
"ct_len": 33,
"ct_sha256": "e7519a9721bc45677ae60f546e6b1909ad89b6bd9041388f5b501efbbfd956de",
"ct_hex": "13485b85b3b4d036ed77edd5eb120afe778bfe094bac290062ec1b004fabc63ceb"
},
{
"size": 65535,
"ct_len": 65551,
"ct_sha256": "89435c31d8a1759df899f6fd7bc9a030ad0ef2d9151b746fb289fe994d9f567a"
},
{
"size": 65536,
"ct_len": 65552,
"ct_sha256": "2b1c36c92c08df57448197d86efb6b321e29141ffb1b241eb3f90961f760a9b6"
},
{
"size": 65537,
"ct_len": 65569,
"ct_sha256": "eb41600f5ad326e7d7056ae89e94bd29a05525e8b471c210af4a1b4275798a09"
},
{
"size": 131072,
"ct_len": 131104,
"ct_sha256": "752ee27990e31a12fe923039a1a7fcd83f267fd3e9a237cab86f6df7575d4350"
},
{
"size": 131073,
"ct_len": 131121,
"ct_sha256": "b93769dc38c7cebe0aa0196e2124f20eff2e50bdab01ed03313dcc29bef39c38"
},
{
"size": 196592,
"ct_len": 196640,
"ct_sha256": "7ca38701023c57deb64762376269ea37f32b8f462fd57db22ba99b3a6a440ba3"
},
{
"size": 300000,
"ct_len": 300080,
"ct_sha256": "ecd2f49ba7cd7d4efc39243d10e9ae4264fc4b5e182a92335587dd54822eefac"
}
]
}