Skip to main content

PROTOCOL

Request for Comments: Vaultys Web of Trust Protocolโ€‹

1. Introductionโ€‹

This document specifies the Vaultys Web of Trust Protocol, a secure mechanism for establishing trust relationships between entities using the Vaultys Decentralized Identity Keyring. The protocol enables mutual authentication, relationship certification, and cryptographic proofs of connection, forming the foundation for a decentralized web of trust.

1.1. Terminologyโ€‹

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

1.2. Abbreviationsโ€‹

  • WoT: Web of Trust
  • DID: Decentralized Identifier
  • SRP: Secure Remote Protocol (in this context, refers to Signing Remote Protocol)
  • PRF: Pseudo-Random Function

2. Protocol Overviewโ€‹

The Vaultys Web of Trust Protocol provides a mechanism for establishing verifiable trust connections between identity holders through the following features:

  1. Challenge-based mutual authentication
  2. Protocol-specific challenge context (protocol/service designation)
  3. Timestamped and liveliness-verified exchanges
  4. Non-repudiable cryptographic signatures
  5. Certificate generation and validation
  6. Metadata exchange
  7. Channel-based secure communications

3. Challenge Formatโ€‹

3.1. Challenge Structureโ€‹

The challenge is the core element of the trust establishment protocol. It is encoded using MessagePack and contains the following fields:

{
"protocol": string, // Protocol identifier
"service": string, // Service identifier
"timestamp": number, // Unix timestamp
"pk1": Buffer, // Public key of initiator
"pk2": Buffer, // Public key of responder
"nonce": Buffer, // Random nonce (16 bytes initially, 32 bytes complete)
"sign1": Buffer, // Signature from initiator
"sign2": Buffer, // Signature from responder
"metadata": object, // Optional context-specific metadata
"state": number // Protocol state
}

3.2. Protocol Statesโ€‹

The challenge progresses through the following states:

State ValueState NameDescription
-2ERRORProtocol failed with an error
-1UNINITIALISEDChallenge not yet initialized
0INITChallenge created, not yet sent
1STEP1Response received from second party
2COMPLETEBoth signatures verified, challenge complete

4. Trust Establishment Protocolโ€‹

4.1. Challenge Creation and Exchangeโ€‹

The trust establishment follows a three-step exchange:

  1. Initiator creates challenge:

    • Generates random nonce (16 bytes)
    • Sets protocol, service, and timestamp
    • Includes initiator's public key (pk1)
  2. Responder processes challenge:

    • Verifies liveliness of timestamp
    • Extends nonce with additional random 16 bytes (total 32 bytes)
    • Adds responder's public key (pk2)
    • Signs the challenge data (sign2)
  3. Initiator completes challenge:

    • Verifies responder's signature
    • Adds initiator's signature (sign1)
    • Challenge is now complete

4.2. Challenge Verificationโ€‹

Each party verifies the other's signature using the following process:

  1. Extract the unsigned challenge data (protocol, service, timestamp, pk1, pk2, nonce)
  2. Verify that the signature corresponds to the hash of this data
  3. Verify that the signature is valid for the counterparty's public key

4.3. Liveliness Verificationโ€‹

To prevent replay attacks, all challenges include liveliness verification:

isLive(challenge, liveliness, time) {
return challenge.timestamp > time - liveliness &&
challenge.timestamp < time + liveliness;
}

Where liveliness defines the acceptable time window (default 60 seconds).

5. Certificate Generation and Storageโ€‹

5.1. Certificate Formatโ€‹

A completed challenge is serialized into a certificate that serves as cryptographic proof of the connection. The certificate contains all challenge fields and is stored in the participants' local databases.

5.2. Certificate Verificationโ€‹

Certificates can be independently verified by third parties using the public keys and signatures they contain:

static async verifyCertificate(certificate: Buffer) {
const deser = deserialize(certificate);
return deser.state === COMPLETE;
}

6. Protocol-Specific Servicesโ€‹

The protocol supports different service types within the challenge context:

6.1. Authentication Service (auth)โ€‹

Establishes mutual authentication and identity verification between two parties.

6.2. Self-Authentication Service (selfauth)โ€‹

Verifies that two devices belong to the same identity owner.

6.3. File Signing Service (signfile)โ€‹

Enables requesting and providing cryptographic signatures for files.

6.4. Transfer Service (transfer)โ€‹

Establishes a secure channel for data transfer between parties.

6.5. Decryption Service (decrypt)โ€‹

Enables secure request and provision of decryption services.

6.6. PRF Service (prf)โ€‹

Enables secure generation and exchange of PRF values for derived keys.

7. Secure Channel Communicationโ€‹

7.1. Channel Interfaceโ€‹

The protocol operates over an abstract Channel interface with the following methods:

interface Channel {
start(): Promise<void>;
close(): Promise<void>;
send(data: Buffer): Promise<void>;
receive(): Promise<Buffer>;
onConnected(callback: () => void): void;
getConnectionString(): string;
fromConnectionString(conn: string, options?: any): Channel | null;
}

7.2. Channel Typesโ€‹

The implementation supports multiple channel types:

  1. MemoryChannel: In-process communication channel
  2. Encrypted Channel: Channel with added encryption layer

7.3. Stream Supportโ€‹

The protocol provides stream-based operations through the StreamChannel wrapper:

interface StreamChannel {
getReadStream(): Readable;
getWriteStream(): Writable;
upload(stream: Readable): Promise<void>;
uploadData(data: Buffer): Promise<void>;
download(stream: Writable): Promise<void>;
downloadData(): Promise<Buffer>;
}

8. Web of Trust Operationsโ€‹

8.1. Contact Managementโ€‹

The protocol enables managing trusted contacts:

// Save a contact to the local contact store
saveContact(contact: VaultysId)

// Get a contact by DID
getContact(did: string): VaultysId | null

// Get all contacts
get contacts(): VaultysId[]

8.2. Certificate Managementโ€‹

Certificates from completed challenges are stored in the local WoT store:

// List all certificates in the local WoT
listCertificates(): ChallengeType[]

// Verify a specific certificate
verifyRelationshipCertificate(did: string): Promise<boolean>

8.3. Contact Metadataโ€‹

The protocol supports attaching and retrieving metadata for contacts:

// Set metadata for a contact
setContactMetadata(did: string, name: string, value: any)

// Get specific metadata for a contact
getContactMetadata(did: string, name: string): any

// Get all metadata for a contact
getContactMetadatas(did: string): object | null

9. Cross-Device Operationsโ€‹

9.1. Device Synchronizationโ€‹

The protocol supports synchronizing data across devices belonging to the same identity:

async sync(channel: Channel, initiator = false)

During synchronization:

  1. Devices authenticate using the selfauth service
  2. Data is exchanged between devices
  3. Contact lists and metadata are merged

9.2. Device Verificationโ€‹

Devices can verify they belong to the same identity:

// Deprecated - use sync instead
async askMyself(channel: Channel): Promise<boolean>
async acceptMyself(channel: Channel): Promise<boolean>

10. Secure Remote Servicesโ€‹

10.1. Remote PRF Generationโ€‹

The protocol enables requesting PRF values from remote devices:

async requestPRF(channel: Channel, appid: string): Promise<Buffer>
async acceptPRF(channel: Channel, accept?: (contact: VaultysId, appid: string) => Promise<boolean>)

10.2. Remote File Operationsโ€‹

The protocol supports secure file encryption, decryption, and signing:

// Remote file encryption/decryption
async requestEncryptFile(channel: Channel, toEncrypt: File): Promise<File | null>
async requestDecryptFile(channel: Channel, toDecrypt: File): Promise<File | null>

// Remote file signing
async requestSignFile(channel: Channel, file: File): Promise<FileSignature | undefined>

11. Security Considerationsโ€‹

11.1. Replay Attacksโ€‹

To prevent replay attacks:

  • Challenges include timestamps
  • Liveliness verification ensures timestamps are within an acceptable window
  • Nonces are used to ensure uniqueness

11.2. Man-in-the-Middle Attacksโ€‹

The protocol mitigates man-in-the-middle attacks through:

  • Mutual authentication
  • Cryptographic signatures
  • Certificate verification

11.3. Key Compromiseโ€‹

If a key is compromised:

  • Existing certificates remain valid
  • New certificates can be issued with updated keys
  • A revocation mechanism should be employed

12. Examplesโ€‹

12.1. Establishing a New Contact Relationshipโ€‹

// Device A (Initiator)
const contactA = await idManagerA.askContact(channel);

// Device B (Responder)
const contactB = await idManagerB.acceptContact(channel);

// Both devices now have a verified contact with a certificate
console.log(contactA.did === idManagerB.vaultysId.did); // true
console.log(contactB.did === idManagerA.vaultysId.did); // true

12.2. Verifying a File Signatureโ€‹

// Request a file signature
const fileSignature = await idManager.requestSignFile(channel, file);

// Verify the signature
const isValid = idManager.verifyFile(file, fileSignature, contact);

12.3. Remote File Encryptionโ€‹

// Request file encryption from a remote device
const encryptedFile = await idManager.requestEncryptFile(channel, file);

// Request file decryption from a remote device
const decryptedFile = await idManager.requestDecryptFile(channel, encryptedFile);

13. Protocol Message Flowโ€‹

13.1. Contact Establishment Flowโ€‹

Initiator                                 Responder
| |
|-- Create challenge -----------------> |
| (protocol, service, pk1, nonce) |
| |
| |-- Process challenge
| | Add pk2, extend nonce
| | Sign challenge
| |
| <------------------- Return challenge |
| (pk1, pk2, sign2) |
| |
|-- Verify sign2 ----------------------> |
| Sign challenge |
| |
| <------------------- Complete challenge
| (pk1, pk2, sign1, sign2)
| |
|-- Store certificate ---------------> |
| |-- Store certificate
| |

13.2. PRF Request Flowโ€‹

Requester                                  Provider
| |
|-- Establish contact (SRP) ----------> |
| |
| <---------------------- Contact established
| |
|-- Send appid ------------------------> |
| |
| |-- Verify appid
| | Generate PRF
| |
| <----------------------------- Send PRF
| |

14. Referencesโ€‹

14.1. Normative Referencesโ€‹

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[MSGPACK] MessagePack, "MessagePack Specification", https://github.com/msgpack/msgpack/blob/master/spec.md

14.2. Informative Referencesโ€‹

[VID-KEYRING] "Vaultys Decentralized Identity Keyring Protocol"

[DID-CORE] W3C, "Decentralized Identifiers (DIDs) v1.0", https://www.w3.org/TR/did-core/

[PGP-WOT] Zimmermann, P., "The Official PGP User's Guide", 1995

Authors' Addressesโ€‹

Franรงois-Xavier Thoorens fx.thoorens@vaultys.com

Appendix A: Challenge Serialization Detailsโ€‹

A.1. MessagePack Serializationโ€‹

Challenges are serialized using MessagePack for compact binary representation. The protocol supports two serialization versions for backward compatibility:

A.1.1. Version 0 Serialization (Legacy)โ€‹

Uses a custom encoding function for challenge fields:

const encode_v0 = ({ protocol, service, timestamp, pk1, pk2, nonce, metadata }) => {
const p = Buffer.concat([
Buffer.from([0x87]), // Map with 7 elements
writeString("protocol", protocol),
writeString("service", service),
writeInt("timestamp", timestamp),
writeBuffer("pk1", pk1),
writeBuffer("pk2", pk2),
writeBuffer("nonce", nonce),
Buffer.from([0xa0 + "metadata".length]),
Buffer.from("metadata", "ascii"),
Buffer.from([0x80]), // empty metadata
]);
return p;
};

A.1.2. Version 1 Serialization (Current)โ€‹

Uses standard MessagePack encoding:

const serialize = (data: ChallengeType) => {
if (data.state == INIT) {
const { protocol, service, timestamp, pk1, nonce, metadata } = data;
const picked = { protocol, service, timestamp, pk1, nonce, metadata };
const encoded = encode(picked); // Standard MessagePack encode
return Buffer.from(encoded);
}
// Other states...
};

A.2. Challenge Deserializationโ€‹

Deserialization includes validation of the challenge structure and state:

const deserialize = (challenge: Buffer): ChallengeType => {
const unpacked = decode(challenge) as ChallengeType;
// ... validation logic ...
return result;
};

Appendix B: Certificate Structureโ€‹

A complete certificate contains:

  1. Protocol identifier (e.g., "p2p")
  2. Service identifier (e.g., "auth", "selfauth")
  3. Timestamp of creation
  4. Public keys of both parties (pk1, pk2)
  5. Combined nonce (32 bytes)
  6. Cryptographic signatures from both parties (sign1, sign2)
  7. Optional metadata

Example binary certificate (hex):

87a8 7072 6f74 6f63 6f6c a370 3270 a773 6572 7669 6365 a461
7574 68a9 7469 6d65 7374 616d 70ce 612c afc1 a370 6b31 c500
20e5 d15b 7f25 9a45 efdc 75b4 1d8a 0895 b21f e1eb 51de bc01
b74c 13ad db76 b362 a370 6b32 c500 20e5 d15b 7f25 9a45 efdc
75b4 1d8a 0895 b21f e1eb 51de bc01 b74c 13ad db76 b362 a56e
6f6e 6365 c500 208b 4367 8763 efb5 9fe9 2ef8 ca58 57a9 92bd
1c86 1bb0 0c72 a6d9 bae2 e3a2 f3cd a673 6967 6e31 c500 40ce
cd11 0f10 1f66 4fa5 01d8 a1e8 ad80 9a7d ffac 26d6 e29c a2fe
9c6a 36ab 42d5 04ef 9f79 2c80 e67c 7b64 bd50 1e5f 84c6 1429
da29 a6d5 e2a9 aa7e 3fff 7ba6 7369 676e 32c5 0040 dbe6 2c68
7c8d baf8 6ec7 0e16 f1dc 3bf5 62a1 29df dfc1 c4ee 3aba 77ab
9d7c f6ef b318 a583 48b9 c94e 5d1e 34c8 eecc 7cd1 ebce 9a8a
d86b 07aa c8a1 095a 16ac a86d 6574 6164 6174 61a2 6161 01

Appendix C: Implementation Notesโ€‹

C.1. Browser Compatibilityโ€‹

The protocol has been tested with:

  • Chrome/Edge
  • Firefox
  • Safari

C.2. Integration with Existing Systemsโ€‹

The protocol can be integrated with existing identity systems by:

  1. Implementing the Channel interface
  2. Providing a VaultysId instance
  3. Following the challenge-response flow

C.3. Performance Considerationsโ€‹

  • Challenges are typically small (< 1KB)
  • Certificate verification is computationally lightweight
  • The protocol is suitable for low-bandwidth, high-latency connections