Skip to content

Latest commit

 

History

History
400 lines (259 loc) · 10 KB

File metadata and controls

400 lines (259 loc) · 10 KB

MX² Specification — Version 1 (MX2:pc:v1)

Status: Draft (Non-Normative Core)
Version: 1.0
Author: Massimo Russo
Last update: 2025-12
License: MIT

1. Introduction

MX² (“MAX 2 eXcryption”) is a portable, password-protected container format for encrypting long-term, high-entropy secret phrases.

MX² is:

  • open
  • verifiable
  • ASCII-safe
  • fully reproducible
  • based exclusively on standardized cryptography

The format is intentionally minimal and suitable for:

  • file storage
  • text encoding
  • QR encoding
  • cross-platform interoperability

This document defines:

  • the MX2:pc:v1 container format
  • the internal JSON record (MAXREC)
  • the password-based encryption mechanism
  • the normative requirements for compliant implementations.

1.1. Security Model (High-Level Overview)

MX² uses a simple but powerful three-layer trust model:

        [1] Password (user secret)
                     │
                     ▼
        [2] MX² Container (encrypted vault)
                     │
                     ▼
        [3] Secret Phrases p1, p2 (root secrets)

[1] Password

  • The only value the user must remember.
  • It does not generate the secret phrases.
  • It merely derives the key that unlocks the container.

[2] MX² Container

  • An encrypted structure holding a JSON record (“MAXREC”).
  • The container is portable and platform-agnostic.

[3] Secret Phrases (p1, p2)

Two long, high-entropy strings stored inside MAXREC.

These phrases act as the root secret from which applications may deterministically derive:

  • encryption keys
  • authentication keys
  • identity keys
  • post-quantum keypairs
  • recovery or migration seeds

MX² does not prescribe any derivation mechanism — this is left to applications.

MX² only defines:

a secure, auditable, password-protected container
for storing p1 and p2.

1.2. Why MX² Is Different

Most cryptographic systems:

  • store keys directly in a vault,
  • derive one deterministic key from a password, or
  • use a seed phrase tied to one specific ecosystem.

MX² introduces a different model:

A portable, password-protected container that stores two high-entropy secret phrases from
which unlimited deterministic keys can be derived — for any purpose and on any implementation.

The password only opens the vault. The phrases are the root secret.

MX² guarantees:

  • no private keys stored on disk
  • unlimited key derivations from a single root
  • long-term recoverability
  • complete auditability
  • interoperability across independent implementations

1.3. Scope and Non-Goals

MX² standardizes only the encrypted container format and the cryptographic mechanism used to protect the internal JSON record (MAXREC).

Implementations and applications:

  • MUST treat p1 and p2 as application-level root secrets;
  • MAY derive p1 and p2 from user input in any way they choose (passwords, hardware tokens, local RNG, external systems, etc.);
  • MAY use p1 and p2 to derive a wide range of keys and identities.

MX² explicitly does not define:

  • how p1 and p2 are generated or managed over time;
  • key management, rotation, or backup policies;
  • multi-device synchronization or account recovery flows;
  • user authentication, identity, or access control.

These aspects are intentionally left to the surrounding application or ecosystem.

2. Normative Keywords

The following terms are to be interpreted as defined in RFC 2119:

  • MUST, MUST NOT
  • SHOULD, SHOULD NOT
  • MAY

3. Design Goals

A compliant implementation MUST satisfy:

  • Portability — MX² must be representable as an ASCII-safe string.
  • Interoperability — any implementation must be able to decrypt payloads generated by others.
  • Transparency — MX² must use only standardized, public cryptographic primitives.
  • Minimalism — the format must contain no unnecessary metadata.
  • Security — secrecy of p1 and p2 must rely on password strength and modern primitives.

4. Container Format Overview

An MX² container MUST use the following structure:

MX2:pc:v1|xchacha20poly1305|salt_b64|nonce_b64|tag_b64|ciphertext_b64

Fields MUST be separated by the ASCII | character.

All fields except the header MUST be base64-encoded.

5. Field Definitions

5.1 Header

Literal value:

MX2:pc:v1

Meaning:

  • MX2: container family
  • pc: Portable Container
  • v1: version 1 of the format

Implementations MUST reject containers with an unknown version.

5.2 Algorithm identifier

Literal value: xchacha20poly1305

Meaning: The AEAD algorithm used for encryption. Implementations MUST reject any value different from "xchacha20poly1305".

5.3 salt_b64

  • Base64 encoding of the Argon2id salt
  • MUST be exactly 16 bytes
  • MUST be unique per container

5.4 nonce_b64

  • Base64 encoding of the XChaCha20 nonce
  • MUST be exactly 24 bytes
  • MUST be generated from a secure RNG
  • MUST NOT repeat under the same key

5.5 tag_b64

  • Base64 encoding of the 16-byte Poly1305 authentication tag

5.6 ciphertext_b64

  • Base64 encoding of the AEAD ciphertext
  • MUST contain a JSON document (MAXREC)
  • MUST be produced using XChaCha20-Poly1305 AEAD

6. Internal JSON Payload (MAXREC)

The decrypted payload MUST be valid UTF-8 JSON.

MAXREC = {
  "type": "MAXREC",         // MUST be literal "MAXREC"
  "v": <uint>,              // version of the record
  "ts": <uint>,             // timestamp (seconds)
  "p1": <string>,           // secret phrase 1
  "p2": <string>,           // secret phrase 2
  "meta": { ... } OPTIONAL  // non-normative
}

Requirements:

  • type MUST be "MAXREC".
  • p1 and p2 MUST be high-entropy secret phrases. The specification does not impose any minimum length: entropy requirements depend on the application.
  • meta MAY contain arbitrary non-critical fields.
  • Implementations MUST ignore unknown fields.

7. Cryptographic Process (Normative)

The MX² encryption pipeline consists of:

  1. Password preprocessing
  2. Key derivation
  3. AEAD encryption
  4. Assembly of the MX² container

7.1 Password Preprocessing

The user password (a UTF-8 string) MUST be transformed into an internal byte string before Argon2id is applied.

The reference construction used in this specification is:

  1. Compute the SHA-256 hash of the password and encode it as a 64-character lowercase hexadecimal string:
hex = SHA256(password)  // 64 hex chars
  1. Let
first8 = the first 8 characters of hex
last8  = the last 8 characters of hex
  1. Derive two internal passcodes:
pass1 = password || "•1" || first8
pass2 = password || "•2" || last8

where all strings are encoded as UTF-8.

  1. Build a single internal byte string pwd_internal as:
pwd_internal = "P1" || 0x00 || pass1 || 0x00 || "P2" || 0x00 || pass2

where "P1" and "P2" are ASCII strings and 0x00 is a single zero byte.

This pwd_internal value is then used as the password input to Argon2id in Section 7.2.

Alternative domain-separated SHA-256 constructions MAY be used in non-interoperable deployments, but implementations that wish to be compatible with the reference Rust and Swift code MUST reproduce this exact pwd_internal construction.

7.2 Key Derivation (Argon2id)

The encryption key MUST be derived using Argon2id with the following parameters:

  • memory: 64 MiB
  • iterations: 3
  • parallelism: 1
  • output length: 32 bytes

Formally:

key32 = Argon2id(
    password   = pwd_internal,
    salt       = salt,
    memory     = 64 MiB,
    iterations = 3,
    parallelism= 1,
    outputLen  = 32
)

where pwd_internal is defined in Section 7.1 and salt is a 16–32 byte random value generated from a secure RNG.

Implementations that wish to decrypt containers produced by the reference code MUST derive key32 exactly as shown above.

7.3 AEAD Encryption (XChaCha20-Poly1305)

Plaintext: UTF-8 JSON (MAXREC)

Associated Data (AAD):

"MAX|MX2|pc|v1"

Nonce: 24 random bytes (MUST NOT repeat for the same key)

Output:

ciphertext, tag = XChaCha20-Poly1305_Encrypt(key32, nonce, plaintext, AAD)

7.4 Assembly

The final MX² container MUST be constructed as:

MX2:pc:v1|xchacha20poly1305|salt_b64|nonce_b64|tag_b64|ciphertext_b64

8. Determinism

MX² is not deterministic as an encryption scheme.

  • The format and key derivation are deterministic.
  • The final ciphertext is randomized because of nonce + salt.

Thus:

  • Two MX² containers created from the same inputs MUST differ.
  • Implementations MUST NOT reuse nonce or salt.

This follows AEAD best practices.

9. Security Considerations (Non-Normative)

  • MX² does not contain private keys — only root secrets.
  • p1 and p2 MUST be treated as highly sensitive values.
  • MX² MUST be stored and transmitted securely.
  • JSON payloads MUST NOT include derivable private keys.
  • Changing p1 or p2 requires generating a new MX² container.
  • The security of MX² depends on password strength and Argon2id parameters.

10. Compatibility and Versions

Current version:

MX2:pc:v1

Planned extensions:

  • MX2:pc:v2 — extended metadata
  • MX2:pc:v3 — binary compact container
  • MX2:EX:v1 — extended eXcryption container

Implementations SHOULD support future version negotiation.

11. Test Vector (Non-Normative Example)

MX2:pc:v1|xchacha20poly1305|2sQ3QzF1zN0=|AAECAwQFBgcICQoLDA0ODxAREhM=|W7Rzuu9J6t5WZg==|p9S0P9uzp8DLiGsQmZq1zknHnNn0ZIqQ2xFZ2w==

This is a synthetic example and MUST NOT be interpreted as a valid container.

12. Reference Implementations

  • Rust implementation (this repository)
  • Swift implementation (MAX App)
  • Python validator (planned)

All implementations MUST adhere to this specification for interoperability.

13. Changelog

v1.0 — Draft

  • Defines the MX2:pc:v1 format
  • Defines MAXREC JSON structure
  • Defines password preprocessing
  • Defines Argon2id parameters
  • Defines AEAD encryption and AAD
  • Introduces normative and non-normative sections