Skip to content

Cross-signing keys

Full source

SampleCrossSigning.java — runnable through mvn -Psample test.

Matrix cross-signing uses three Ed25519 key pairs per user — the master key (MSK), the user-signing key (USK) and the self-signing key (SSK). This sample reproduces the specification's signature chains with Ed25519KeyPair.

1. Generate the three signing identities

try (Ed25519KeyPair masterKeyPair = new Ed25519KeyPair();
        Ed25519KeyPair userSigningKeyPair = new Ed25519KeyPair();
        Ed25519KeyPair selfSigningKeyPair = new Ed25519KeyPair();
        Account account = new Account()) { // the device

    // spec-shaped key objects: keys, usage, user_id
    Json masterKey = buildMatrixKey(masterKeyPair.publicKey(), "master");
    Json userSigningKey = buildMatrixKey(userSigningKeyPair.publicKey(), "user_signing");
    Json selfSigningKey = buildMatrixKey(selfSigningKeyPair.publicKey(), "self_signing");

    // persist each signing key (JSON holding the private key):
    String mskPickle = masterKeyPair.pickle();

2. The master key signs the USK and the SSK

    String mskKeyId = "ed25519:" + masterKeyPair.publicKey().toBase64();

    Json signedSelfSigningKey =
            addSignature(selfSigningKey, mskKeyId, masterKeyPair.sign(selfSigningKey.toString()));
    Json signedUserSigningKey =
            addSignature(userSigningKey, mskKeyId, masterKeyPair.sign(userSigningKey.toString()));

3. The device keys are signed twice

By the device's own Ed25519 key and by the self-signing key — this is what makes a device "trusted" under cross-signing:

    Json device = buildMatrixDevice(account);
    String deviceKeyId = "ed25519:" + DEVICE_ID;
    String sskKeyId = "ed25519:" + selfSigningKeyPair.publicKey().toBase64();

    Json signedByAccount =
            addSignature(device, deviceKeyId, account.sign(device.toString()));
    Json signedDevice =
            addSignature(signedByAccount, sskKeyId, selfSigningKeyPair.sign(device.toString()));

4. The device key signs the master key, then everything is uploaded

    Json signedMasterKey =
            addSignature(masterKey, deviceKeyId, account.sign(masterKey.toString()));

    Json crossSigningKeys = Json.object()
            .set("master_key", signedMasterKey)
            .set("self_signing_key", signedSelfSigningKey)
            .set("user_signing_key", signedUserSigningKey);
    // upload crossSigningKeys + the signed device keys to /keys/upload

Persistence

There is no seed API: generate each key pair once and treat pickle() as the recoverable secret (JSON containing the private key — store it in your secret storage, restore with Ed25519KeyPair.unpickle(...)).