Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ package replaces it in one step (`Conflicts`/`Replaces: passman`), and your
vaults stay in `~/.local/share/passman`, which SecretSpace keeps using —
nothing is moved or renamed. The vault files are the same.

**`main` is 1.0.0-alpha.5, a SecretSpace 3 test version — not for real passwords yet.**
**`main` is 1.0.0-alpha.6, a SecretSpace 3 test version — not for real passwords yet.**
For real use keep the [0.11.0 release](https://github.com/usermaneGermane/SecretSpace-Client-Linux/releases/tag/v0.11.0),
which the rest of this README describes. It installs from a `.deb` on Ubuntu
22.04 or newer, or runs from source. It has so far been tested in CI only,
Expand All @@ -31,14 +31,14 @@ on a virtual display, not yet on a real desktop.
**Same vaults as on Windows.** The vault file, the sync protocol and the
features match SecretSpace for Windows (see the version table under *What is
different from Windows*): 0.11.0 = Windows 2.13.0, and this test version =
Windows 3.0.0-alpha.5.
Windows 3.0.0-alpha.6.
A vault moves between a Windows PC and a Linux PC unchanged, and both sync
with either server.

## SecretSpace 3 — the test version

1.0.0-alpha.5 is the same as [SecretSpace for Windows
3.0.0-alpha.5](https://github.com/usermaneGermane/SecretSpace-Client-Windows#secretspace-3--a-test-version-not-for-real-passwords-yet):
1.0.0-alpha.6 is the same as [SecretSpace for Windows
3.0.0-alpha.6](https://github.com/usermaneGermane/SecretSpace-Client-Windows#secretspace-3--a-test-version-not-for-real-passwords-yet):
accounts with a Secret Key and a 12-word recovery phrase, Argon2id, and
importing 2.x vaults with their history. It opens SecretSpace 3 accounts only,
so do not install it over 0.11.0 — your vaults would not show up (they are
Expand All @@ -47,7 +47,12 @@ not changed). Since alpha.3 it syncs through a SecretSpace Server 2.0
from a server*. Since alpha.5 an account can have **several vaults**
(*Vaults* above the list): each with its own key, picked in the menu above
the list, and all covered by **Sync** — which needs SecretSpace Server
2.0.0-alpha.2 for more than the Personal vault. On Linux:
2.0.0-alpha.2 for more than the Personal vault. Since alpha.6 a vault can be
**shared** with other accounts of the same server (*Vaults → Members*:
owner, editor or reader, a fingerprint check before the first share, and
*Remove* gives the vault a new key), and every vault keeps a signed record
of **who changed what** (*Vaults → Activity*) — never of who viewed or
copied a password. Sharing needs SecretSpace Server 2.0.0-alpha.3. On Linux:

- **The Secret Key is kept under your master password** (Argon2id, then
AES-GCM), not by the system as Windows does with DPAPI. So a copy of the
Expand All @@ -57,7 +62,7 @@ the list, and all covered by **Sync** — which needs SecretSpace Server
- The account folders are `700` and their files `600`, like the vault
folder; a saved Emergency Kit is `600` too.
- *Settings → Backup → Open folder* opens the folder with `xdg-open`.
- The package spells the version `1.0.0~alpha.5`, which apt sorts after
- The package spells the version `1.0.0~alpha.6`, which apt sorts after
0.9.0 and before 1.0.0.

| File | What it is |
Expand Down Expand Up @@ -191,6 +196,7 @@ Where that README names a Windows version, read the Linux one beside it:
| 3.0.0-alpha.3 | 1.0.0-alpha.3 | accounts sync through SecretSpace Server 2.0; sign in from a server |
| 3.0.0-alpha.4 | 1.0.0-alpha.4 | a smoother look: smoothed corners, redrawn sign-in backdrop |
| 3.0.0-alpha.5 | 1.0.0-alpha.5 | several vaults per account; Sync covers them all (Server 2.0.0-alpha.2) |
| 3.0.0-alpha.6 | 1.0.0-alpha.6 | sharing a vault (owner / editor / reader), who changed what (Server 2.0.0-alpha.3) |

(0.2.0 added the `.deb` package, which has no Windows counterpart.)

Expand Down
45 changes: 45 additions & 0 deletions sourceFiles/account_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import unicodedata

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import (Ed25519PrivateKey,
Ed25519PublicKey)
from cryptography.hazmat.primitives.asymmetric.x25519 import (X25519PrivateKey,
X25519PublicKey)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
Expand Down Expand Up @@ -349,6 +351,49 @@ def fingerprint(public_key: bytes) -> str:
return " ".join(digits[i:i + 4] for i in range(0, 24, 4))


def account_fingerprint(public_key: bytes, signing_key: bytes) -> str:
"""
The fingerprint two people compare before the first share: it covers
both of an account's public keys, so one check vouches for the key a
vault key is wrapped to *and* the key the account's changes are signed
with.
"""
return fingerprint(public_key + signing_key)


# ===========================================================================
# Signing (the change log, phase 4)
# ===========================================================================
def _signing_private(keyset: dict) -> Ed25519PrivateKey:
# Derived from the account's private key rather than stored beside it:
# every keyset ever made already has one, the recovery copy included,
# and there is nothing new to keep in step. HKDF keeps the two keys
# independent in use.
seed = _hkdf(_unb64(keyset["x25519_private"], "account key"),
b"PassMan3 signing", b"PassMan3 signing key")
return Ed25519PrivateKey.from_private_bytes(seed)


def signing_key_of(keyset: dict) -> bytes:
"""The public half of the account's signing key (32 bytes)."""
return _signing_private(keyset).public_key().public_bytes(
serialization.Encoding.Raw, serialization.PublicFormat.Raw)


def sign(keyset: dict, message: bytes) -> str:
return _b64(_signing_private(keyset).sign(message))


def verify(signing_key: bytes, message: bytes, signature: str) -> bool:
"""Did the owner of ``signing_key`` sign ``message``? Never raises."""
try:
Ed25519PublicKey.from_public_bytes(signing_key).verify(
base64.b64decode(signature, validate=True), message)
return True
except Exception:
return False


# ===========================================================================
# PMV3: a vault's entries under its own key
# ===========================================================================
Expand Down
Loading
Loading