Encryption and Security
When exchanging sensitive information such as personal data, private keys, or verifiable credentials, security is paramount. The did-auth library provides a built-in mechanism for establishing an encrypted communication channel between your application and the user's DID Wallet, ensuring that sensitive data is protected end-to-end.
This section explains how the encryption flow works and how you can leverage it to handle sensitive claims securely. For a practical example of requesting a sensitive claim, see the Request a Signature guide.
The Encryption Flow#
The did-auth-protocol version 1.0.0 introduces an end-to-end encryption mechanism. The process is largely automated by the library. When a wallet supporting this protocol version scans a QR code, it initiates the secure flow.
Here is a diagram illustrating the key exchange and encrypted communication process:
This process combines asymmetric encryption for securely exchanging a session key and symmetric encryption for efficient and secure data transfer thereafter.
Enabling Encryption#
Encryption is automatically enabled when the wallet signals its support by providing its public key (_ek_ parameter) and a compatible version (_v_ parameter) during the initial request. The server then uses this to establish the secure channel.
Requesting Sensitive Claims#
Your application doesn't need to manually call encryption functions for claims. The library handles it when you request claims that are inherently sensitive, such as a keyPair claim.
Here is an example of setting up a handler that requests the wallet to generate a new key pair. The secret key of this pair will be transmitted over the encrypted channel.
// did/did-auth/tests/handlers/wallet.spec.js
const handlers = new WalletHandlers({ tokenStorage, authenticator });
handlers.attach({
app: server,
action: 'encrypted',
onConnect: () => {
return [
{
keyPair: () => {
return {
mfa: true, // Requires multi-factor authentication
description: 'generate keypair',
moniker: 'test',
targetType: {
role: 'application',
},
};
},
},
];
},
onAuth: ({ claims }) => {
const keyPair = claims.find((x) => x.type === 'keyPair');
// The `keyPair.secret` was received securely.
console.log('Received secret key:', keyPair.secret);
return { successMessage: 'Key pair generated successfully!' };
},
});In this flow, the wallet will encrypt the response containing the generated private key using the sharedKey before sending it to your application. The did-auth library automatically decrypts the incoming payload.
Core Encryption Functions#
The library exposes encrypt and decrypt functions, though you typically won't need to call them directly. They are used internally by the handlers.
encrypt(data, config, dataKey = 'authInfo'): Encrypts thedata[dataKey]field ifdata.sensitiveis true and asharedKeyis present in theconfig.decrypt(data, config, dataKey = 'userInfo'): Decrypts thedata[dataKey]field if asharedKeyis present in theconfig.
Here is how they behave based on the test cases:
// From did/did-auth/tests/protocol.spec.js
const sharedKey = 'abcd';
const clientVersion = '1.0.0';
// Encryption is triggered by the `sensitive: true` flag
const encrypted = encrypt(
{ sensitive: true, authInfo: 'some-payload' },
{ clientVersion, sharedKey }
);
// encrypted.authInfo is now a base58 encoded encrypted string
// Decryption
const decrypted = decrypt(
{
userInfo: encrypted.authInfo, // Using the encrypted string from above
version: '1.0.0'
},
{ sharedKey }
);
// decrypted.userInfo is now 'some-payload'Securely Storing Session Data#
In some cases, you may need to store sensitive information within the session on the server side that isn't part of a claim. The updateSession function, available in the handler callbacks, supports a secure flag for this purpose.
When you update the session with secure: true, the data is encrypted using the wallet's public key (encryptionKey) before being stored. This ensures that even if the session storage is compromised, the sensitive data remains protected.
// did/did-auth/tests/handlers/wallet.spec.js
// Inside an `onAuth` callback for example:
onAuth: async ({ updateSession }) => {
const unsecure = 'unsecure_data';
const secure = 'secure_data';
const secureObj = { secret: 'is_safe' };
// Store data as-is
await updateSession('unsecure', unsecure);
// Store data securely
await updateSession({ secure, secureObj }, true);
return { successMessage: 'Data stored' };
}This uses tweetnacl-sealedbox-js to perform public-key authenticated encryption, providing strong guarantees about the confidentiality and integrity of the stored data.
By leveraging these built-in features, you can ensure a high level of security for your DID-based authentication flows.
Next, you may want to learn more about the different types of information you can request from a user by reading about Understanding Claims.