A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
package wordkey
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
|
"slices"
|
|
|
|
|
|
"strings"
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
|
|
"g.activething.com/go/DateKeys/profile"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func TestNormalize(t *testing.T) {
|
|
|
|
|
|
nbsp, tab := string(rune(0xa0)), string(rune(9))
|
|
|
|
|
|
in := " Ábaco" + nbsp + "ÁRBOL" + tab + "niño ΣΑΣ İ "
|
|
|
|
|
|
want := []string{"abaco", "arbol", "nino", "σασ", "i"}
|
|
|
|
|
|
if got := Normalize(in); !slices.Equal(got, want) {
|
|
|
|
|
|
t.Fatalf("Normalize = %q, want %q", got, want)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := Normalize(" "); len(got) != 0 {
|
|
|
|
|
|
t.Fatalf("Normalize of spaces = %q", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
// The lower case of Unicode 18.0.0, not that of the runtime: U+A7CB,
|
|
|
|
|
|
// added in Unicode 16.0, lowers to U+0264, which Go 1.26 does not know.
|
|
|
|
|
|
if got := Normalize(string(rune(0xA7CB))); !slices.Equal(got, []string{string(rune(0x0264))}) {
|
|
|
|
|
|
t.Fatalf("Normalize of U+A7CB = %q", got)
|
|
|
|
|
|
}
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func TestCheck(t *testing.T) {
|
|
|
|
|
|
if err := Check(Normalize("Perro LUNA casa verde trén mar")); err != nil {
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
const six = "perro luna casa verde tren mar"
|
|
|
|
|
|
for _, tc := range []struct{ text, want string }{
|
|
|
|
|
|
{"uno dos tres", "not 3"},
|
|
|
|
|
|
{"a b c d e f g h", "not 0"},
|
|
|
|
|
|
{"perro perro perro perro perro perro", "not 1"},
|
|
|
|
|
|
{"de la casa al mar en tren verde", "not 4"},
|
|
|
|
|
|
{six + string(rune(0x200B)), "invisible character U+200B"},
|
|
|
|
|
|
{six + string(rune(0x0001)), "control character U+0001"},
|
|
|
|
|
|
{six + string(rune(0x0378)), "U+0378, unassigned"},
|
|
|
|
|
|
} {
|
|
|
|
|
|
if err := Check(Normalize(tc.text)); err == nil || !strings.Contains(err.Error(), tc.want) {
|
|
|
|
|
|
t.Errorf("Check(%q) = %v, want %q", tc.text, err, tc.want)
|
|
|
|
|
|
}
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The vector of spec §38.1, which wordkey.test.ts of datekeys-ts checks too.
|
|
|
|
|
|
func TestKeyVector(t *testing.T) {
|
|
|
|
|
|
chain, _ := hex.DecodeString(profile.QuicknetChainHash)
|
|
|
|
|
|
capsuleID, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f")
|
|
|
|
|
|
words := Normalize("perro luna casa verde tren mar")
|
|
|
|
|
|
raw, err := Key(words, chain, 1000, capsuleID)
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if got := hex.EncodeToString(raw); got != "fceec4d8ca8de86c85a1f26ed49f82a2b38431bd0ce36db995ae7dfd49b96e41" {
|
|
|
|
|
|
t.Fatalf("Key = %s", got)
|
|
|
|
|
|
}
|
|
|
|
|
|
id, err := Identity(words, chain, 1000, capsuleID)
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
otherCapsule := slices.Clone(capsuleID)
|
|
|
|
|
|
otherCapsule[15] ^= 1
|
|
|
|
|
|
for _, o := range []struct {
|
|
|
|
|
|
name string
|
|
|
|
|
|
round uint64
|
|
|
|
|
|
id []byte
|
|
|
|
|
|
}{{"round", 1001, capsuleID}, {"capsule_id", 1000, otherCapsule}} {
|
|
|
|
|
|
other, err := Identity(words, chain, o.round, o.id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
}
|
|
|
|
|
|
if id.Recipient().String() == other.Recipient().String() {
|
|
|
|
|
|
t.Errorf("the %s does not change the key", o.name)
|
|
|
|
|
|
}
|
A key of words for the CLI: encrypt and decrypt with -words
The author asked for keys that people can keep without files. Package
wordkey derives the X25519 identity of words a person chooses, at least
six: their NFD by the tables of pathrule without the marks U+0300 to
U+036F, each code point in lower case by its simple mapping, split at
white space, joined by one space, and stretched with PBKDF2-HMAC-SHA256
of the standard library, 600 000 rounds, salted with the chain hash and
the round of the capsule. It is wordkey.ts of datekeys-ts byte for byte:
both check the same vector.
encrypt takes -words or -words-file for a time_and_key capsule, and adds
the key as one more recipient, derived for the round of -at; decrypt
takes them and adds the identity, for the round the capsule shows. The
format does not change. Checked: the CLI opened a capsule that the page
wrote with words, with the release from the public relays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
|
|
|
|
}
|
|
|
|
|
|
}
|