Skip to content

Attributes encryption

Cloudproof Encryption provides libraries and tools to encrypt and securely index large repositories of data - Big Data - with high performance and advanced security primitives.

See the use cases and benefits and a description of the cryptosystems used.

The libraries are available in multiple languages facilitating encryption close to the data source and decryption close to the decryption target, including mobile devices and browsers.

This section describes the Attributes Encryption API, the Searchable Encryption API is on this page

Setup

It is possible to create keys using the libraries; however, it is more likely that you will want to store them in a KMS. Please go to the KMS section to fetch and run a local KMS.

The library is published on npm: simply install the library in your package.json

npm install cloudproof_js

The library contains web assembly for cryptographic speed

You need Java 8+ and Maven version 3+ The cloudproof_java library is deployed on Maven central. Add the library to your POM

<dependency>
  <groupId>com.cosmian</groupId>
  <artifactId>cloudproof_java</artifactId>
  <version>2.0.0</version>
  <type>jar</type>
</dependency>

Encryption and decryption can be accomplished in the KMS, however it is likely that you will want to perform them locally. This is accomplished using the native library, compiled from Rust. The cloudproof_java uses Java Native Access to call the native library.

Please see the CoverCrypt project on how build the library. In your java project, the library needs to be placed inside in

  • src/main/resources/linux-x86-64 folder for a Linux Intel machine
  • src/main/resources/linux-amd64 folder for a Linux AMD machine
  • src/main/resources/darwin folder for a Mac running MacOS
  • src/main/resources/win32-x86 folder for a Windows machine

To run tests only, place the library inside the corresponding folder above replacing main with test

Check the JNA documentation for alternative locations for the native library.

Coming soon!

Creating a policy

A Policy is specified by Policy Axes, defining a n dimensional matrix of authorizations/partitions. A user must possess keys with access policies satisfying the attributes from these n axes to be able to decrypt files.

In the example below, we create a Policy which combines two axes, a security level axis and a department axis. A user will be able to decrypt data only if it possesses a key with a sufficient security level and the code for the department.

const policy = new Policy([
    new PolicyAxis(
        "Department",
        ["R&D", "HR", "FIN", "MKG"],
        false
    ),
    new PolicyAxis(
        "Security Level",
        ["Protected", "Low Secret", "Medium Secret", "High Secret", "Top Secret"],
        true
    ),
])
Policy policy = new Policy(Integer.MAX_VALUE)
     // Integer.MAX_VALUE is an arbitrary value that represents
     // the maximum number of attributes that can be
     // used in policy
    .addAxis("Security Level",
            new String[] {
                "Protected",
                "Low Secret",
                "Medium Secret",
                "High Secret",
                "Top Secret"
                },
            true) // <- hierarchical axis
    .addAxis("Department",
            new String[] {
                "R&D",
                "HR",
                "MKG",
                "FIN" },
            false); // <- non hierarchical axis

Coming soon!

Security Level Axis

The first Policy Axis is the ‘Security Level’ axis and is a hierarchical axis made of 5 levels: Protected, Low Secret, ..., Top Secret.

It is hierarchical: a user being granted access to level n is automatically granted access to all levels below n. The attributes must be provided in ascending order.

Department Security Axis

The second Policy Axis is the Department axis and is made of 4 values: R&D, HR, MKG, FIN. This axis is not hierarchical: granting access to an attribute of this axis to a user does not give access to any other attribute. Each attribute must be granted individually.

Generating the master keys

The Master Authority possesses the key pair for the given Policy:

  • a Master Private Key which is used to generate user keys
  • and a Public Key which is used to encrypt files with any combination of attributes. Since it cannot decrypt, the public key can be freely distributed to encrypting systems.

To generate keys you need access to a Cosmian KMS server and, unless you run in dev mode, an API Key to authenticate to the server.

const client = new KmipClient(new URL("http://localhost:9998/kmip/2_1"))

// create master keys
const [privateMasterKeyUID, publicKeyUID] =
  await client.createAbeMasterKeyPair(policy)

// fetch the keys from the KMS
const privateMasterKey = await client.retrieveAbePrivateMasterKey(
  privateMasterKeyUID
)
// eslint-disable-next-line no-unused-vars
const privateMasterKeyBytes = privateMasterKey.bytes()
const publicKey = await client.retrieveAbePublicMasterKey(publicKeyUID)
const publicKeyBytes = publicKey.bytes()

Keys can also be generated locally using the WASM code

const { CoverCryptKeyGeneration } = await CoverCrypt();

const keygen = new CoverCryptKeyGeneration();
const masterKeysCoverCrypt = keygen.generateMasterKeys(policy);
const privateMasterKeyBytes = masterKeysCoverCrypt.secretKey
const publicKeyBytes = masterKeysCoverCrypt.publicKey

To generate keys you need access to a Cosmian KMS server and, unless you run in dev mode, an API Key to authenticate to the server.

Abe client = new Abe(
    new RestClient([KMS_SERVER_URL], [YOUR_API_KEY])
);

// create the Master Keys
String[] ids = client.createMasterKeyPair(policy);

// recover the Keys
String privateMasterKeyUID = ids[0];
PrivateKey privateMasterKey = client.retrievePrivateMasterKey(privateMasterKeyUID);
byte[] privateMasterKeyBytes = privateMasterKey.bytes();
String publicKeyUID = ids[1];
PrivateKey publicKey = client.retrievePublicMasterKey(publicMasterKeyUID);
byte[] publicKeyBytes = publicKey.bytes();
static final FfiWrapper INSTANCE = (FfiWrapper) Native.load("cosmian_cover_crypt", FfiWrapper.class);
static final Ffi ffi = new Ffi(INSTANCE, new Specifications(Implementation.CoverCrypt));

MasterKeys masterKeys = ffi.generateMasterKeys(policy);
byte[] privateMasterKeyBytes =  masterKeys.getPrivateKey()
byte[] publicKeyBytes =  masterKeys.getPublicKey()

Coming soon!

Encrypting Data

Data is encrypted using the Master Authority Public Key and at least one attribute from each Policy axis.

Anyone who has access to the Public Key, can encrypt data, however, only users possessing user keys with the right access policy can decrypt data.

This shows the encryption of 3 messages with different attributes.

Encrypting data is done using the encrypt() function that concatenates the header (encapsulation of the ephemeral symmetric key) and the symmetrically encrypted data.

These can be done separately; see the CoverCryptHybridEncryption class

a low secret marketing message

const { CoverCryptHybridEncryption } = await CoverCrypt();

const lowSecretMkgData = new TextEncoder().encode("low_secret_mkg_message")
// The constructor also accepts the public key object returned by the KMS
let encrypter = new CoverCryptHybridEncryption(policy, publicKeyBytes)
const lowSecretMkgCiphertext = encrypter.encrypt(
  "Department::MKG && Security Level::Low Secret",
  lowSecretMkgData
)

a top secret marketing message

const { CoverCryptHybridEncryption } = await CoverCrypt();

const topSecretMkgData = new TextEncoder().encode("top_secret_mkg_message")
encrypter = new CoverCryptHybridEncryption(policy, publicKeyBytes)
const topSecretMkgCiphertext = encrypter.encrypt(
  "Department::MKG && Security Level::Top Secret",
  topSecretMkgData
)

a low secret finance message

const { CoverCryptHybridEncryption } = await CoverCrypt();

const lowSecretFinData = new TextEncoder().encode("low_secret_fin_message")
encrypter = new CoverCryptHybridEncryption(policy, publicKeyBytes)
const lowSecretFinCiphertext = encrypter.encrypt(
  "Department::FIN && Security Level::Low Secret",
  lowSecretFinData
)

Encrypting using the native library is fast and provides many options

a low secret marketing message

byte[] lowSecretMkgData = "low_secret_mkg_message".getBytes(StandardCharsets.UTF_8);
String lowSecretMkgEncryptionPolicy = "Department::MKG && Security Level::Low Secret";
byte[] lowSecretMkgCiphertext = ffi.encrypt(
    policy, masterKeys.getPublicKey(), 
    lowSecretMkgEncryptionPolicy, lowSecretMkgData
);

a top secret marketing message

byte[] topSecretMkgData = "top_secret_mkg_message".getBytes(StandardCharsets.UTF_8);
String topSecretMkgEncryptionPolicy = "Department::MKG && Security Level::Top Secret";
byte[] topSecretMkgCiphertext = ffi.encrypt(
    policy, masterKeys.getPublicKey(), 
    topSecretMkgEncryptionPolicy, topSecretMkgData
);

a low secret finance message

byte[] lowSecretFinData = "low_secret_fin_message".getBytes(StandardCharsets.UTF_8);
String lowSecretFinEncryptionPolicy = "Department::FIN && Security Level::Low Secret";
byte[] lowSecretFinCiphertext = ffi.encrypt(
    policy, masterKeys.getPublicKey(), 
    lowSecretFinEncryptionPolicy, lowSecretFinData
);

Coming soon!

Generating User Decryption Keys

User Decryption Keys are generated from the Master Private Key using Access Policies. Access Policies are monotonous boolean expressions combining policy attributes.

Let us create 2 users with their different access policies to illustrate their effect.

the medium secret marketing user

This user can decrypt messages from the marketing department only with a security level of Medium Secret or below:

const mediumSecretMkgAccess =
  "Department::MKG && Security Level::Medium Secret"
const mediumSecretMkgUserKeyUid = await client.createAbeUserDecryptionKey(
  mediumSecretMkgAccess,
  privateMasterKeyUID
)
const mediumSecretMkgUserKey = await client.retrieveAbeUserDecryptionKey(
  mediumSecretMkgUserKeyUid
)
const mediumSecretMkgUserKeyBytes = mediumSecretMkgUserKey.bytes()

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department that have a security level of Top Secret or below

const topSecretMkgFinAccess =
  "(Department::MKG || Department::FIN) && Security Level::Top Secret"
const topSecretMkgFinUserKeyUid = await client.createAbeUserDecryptionKey(
  topSecretMkgFinAccess,
  privateMasterKeyUID
)
const topSecretMkgFinUserKey = await client.retrieveAbeUserDecryptionKey(
  topSecretMkgFinUserKeyUid
)
const topSecretMkgFinUserKeyBytes = topSecretMkgFinUserKey.bytes()

the top secret - only - financial user

This user can decrypt messages from the financial department that have a security level of Top Secret or below

const topSecretFinAccess = "(Department::FIN) && Security Level::Top Secret"
const topSecretFinUserKeyUid = await client.createAbeUserDecryptionKey(
  topSecretFinAccess,
  privateMasterKeyUID
)
const topSecretFinUserKey = await client.retrieveAbeUserDecryptionKey(
  topSecretFinUserKeyUid
)
// eslint-disable-next-line no-unused-vars
const topSecretFinUserKeyBytes = topSecretFinUserKey.bytes()

User Decryption Keys can also be generated calling the local Web Assembly

the medium secret marketing user

This user can decrypt messages from the marketing department only with a security level of Medium Secret or below:

const { CoverCryptKeyGeneration } = await CoverCrypt();
const keygen = new CoverCryptKeyGeneration()

const mediumSecretMkgUserKeyBytes =
    keygen.generateUserSecretKey(
        masterSecretKeyBytes,
        "Department::MKG && Security Level::Medium Secret",
        policy,
    )

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department that have a security level of Top Secret or below

const topSecretMkgFinUserKeyBytes =
    keygen.generateUserDecryptionKey(
        masterSecretKeyBytes,
        "(Department::MKG || Department::FIN) && Security Level::Medium Secret",
        policy,
    )

the medium secret marketing user

This user can decrypt messages from the marketing department only with a security level of Medium Secret or below:

AccessPolicy mediumSecretMkgAccess = new And(
    new Attr("Department", "MKG"),
    new Attr("Security Level", "Medium Secret"),
);
String mediumSecretMkgUserKeyUid =
    client.createUserDecryptionKey(
        mediumSecretMkgAccess,
        privateMasterKeyUID,
    );
PrivateKey mediumSecretMkgUserKey = client.retrieveUserDecryptionKey(mediumSecretMkgUserKeyUid);

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department that have a security level of Top Secret or below

AccessPolicy topSecretMkgFinAccess =
    new And(
        new Or(new Attr("Department", "MKG"), new Attr("Department", "FIN")),
        new Attr("Security Level", "Top Secret"),
    );
String topSecretMkgFinUserKeyUid =
    client.createUserDecryptionKey(
        topSecretMkgFinAccess,
        privateMasterKeyUID,
    );
PrivateKey topSecretMkgFinUserKey = client.retrieveUserDecryptionKey(topSecretMkgFinUserKeyUid);

User keys can be locally generated using the native library

the medium secret marketing user

This user can decrypt messages from the marketing department only with a security level of Medium Secret or below:

AccessPolicy mediumSecretMkgAccess = new And(
    new Attr("Department", "MKG"),
    new Attr("Security Level", "Medium Secret"),
);
byte[] mediumSecretMkgUserKeyBytes =
    ffi.generateUserPrivateKey(
        privateMasterKeyBytes,
        mediumSecretMkgAccess,
        policy,
    );

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department that have a security level of Top Secret or below

AccessPolicy topSecretMkgFinAccess =
    new And(
        new Or(new Attr("Department", "MKG"), new Attr("Department", "FIN")),
        new Attr("Security Level", "Top Secret")
    );
byte[] topSecretMkgFinUserKeyBytes =
    ffi.generateUserPrivateKey(
        privateMasterKeyBytes,
        topSecretMkgFinAccess,
        policy,
    );

Coming soon!

Decrypting Ciphertexts

The medium secret marketing user can successfully decrypt a low security marketing message:

const { CoverCryptHybridDecryption } = await CoverCrypt();

//  note: the constructor also accepts the private key object returned by the KMS
const lowSecretMkgCleartext = new CoverCryptHybridDecryption(
  mediumSecretMkgUserKeyBytes
).decrypt(lowSecretMkgCiphertext)
assert(lowSecretMkgCleartext, lowSecretMkgData)
byte[] data = ffi.decrypt(
    mediumSecretMkgUserKey, 
    lowSecretMkgCiphertext
);
assertArrayEquals(
    lowSecretMkgData,
    data,
);

Coming soon!

.. however it can neither decrypt a marketing message with higher security:

const { CoverCryptHybridDecryption } = await CoverCrypt();

try {
  // will throw
  new CoverCryptHybridDecryption(mediumSecretMkgUserKey).decrypt(
    topSecretMkgCiphertext
  )
} catch (error) {
  // ==> the user is not be able to decrypt
}
try {
    // will throw
    ffi.decrypt(
        mediumSecretMkgUserKey.bytes(), 
        topSecretMkgCiphertext
    );
} catch (CosmianException e) {
    // ==> the user is not be able to decrypt
}

Coming soon!

… nor decrypt a message from another department even with a lower security:

const { CoverCryptHybridDecryption } = await CoverCrypt();

try {
  // will throw
  new CoverCryptHybridDecryption(topSecretFinUserKey).decrypt(
    lowSecretMkgCiphertext
  )
} catch (error) {
  // ==> the user is not be able to decrypt
}
try {
    ffi.decrypt(
        mediumSecretMkgUserKey.bytes(), 
        lowSecretFinCiphertext
    );
      // will throw
} catch (CosmianException e) {
    // ==> the user is not be able to decrypt
}

Coming soon!

As expected, the top secret marketing financial user can successfully decrypt all messages

const { CoverCryptHybridDecryption } = await CoverCrypt();

// lowSecretMkgCiphertext
const lowSecretMkgCleartext2 = new CoverCryptHybridDecryption(
  topSecretMkgFinUserKeyBytes
).decrypt(lowSecretMkgCiphertext)
assert(lowSecretMkgData, lowSecretMkgCleartext2)

// lowSecretFinCiphertext
const topSecretMkgCleartext = new CoverCryptHybridDecryption(
  topSecretMkgFinUserKeyBytes
).decrypt(topSecretMkgCiphertext)
assert(topSecretMkgData, topSecretMkgCleartext)

// lowSecretFinCiphertext
const lowSecretFinCleartext = new CoverCryptHybridDecryption(
  topSecretMkgFinUserKeyBytes
).decrypt(lowSecretFinCiphertext)
assert(lowSecretFinData, lowSecretFinCleartext)
// lowSecretMkgCiphertext
byte[] data = ffi.decrypt(
    top_secret_mkg_fin_user_key.bytes(), 
    lowSecretMkgCiphertext
);
assertArrayEquals(
    lowSecretMkgData,
    data
);

// topSecretMkgCiphertext
byte[] data = ffi.decrypt(
    top_secret_mkg_fin_user_key.bytes(), 
    topSecretMkgCiphertext
);
assertArrayEquals(
    topSecretMkgData,
    data
);

// lowSecretFinCiphertext
byte[] data = ffi.decrypt(
    top_secret_mkg_fin_user_key.bytes(), 
    lowSecretFinCiphertext
);
assertArrayEquals(
    lowSecretFinData,
    data
);

Coming soon!

Rotating Attributes

At anytime the Master Authority can rotate an attribute.

When that happens future encryption of data for a given attribute cannot be decrypted with keys which are not “refreshed” for that attribute.

We are going to rotate the Department::MKG attribute.

As long as a key is active in the KMS (the key has not been revoked), it will be automatically rekeyed when an attribute is rotated.

rotating the Department::MKG attribute

Before rotating attributes, let us make a local copy of the current medium secret marketing user to show what happens to non-refreshed keys after the attribute rotation.

// retrieve the key
const originalMediumSecretMkgUserKey =
  await client.retrieveAbeUserDecryptionKey(mediumSecretMkgUserKeyUid)

// Now revoke the MKG attribute - all active keys will be rekeyed
client.rotateAbeAttributes(privateMasterKeyUID, ["Department::MKG"])

// retrieve the rekeyed public key
const rekeyedPublicKey = await client.retrieveAbePublicMasterKey(publicKeyUID)
// retrieve the rekeyed user decryption key
const rekeyedMediumSecretMkgUserKey =
  await client.retrieveAbeUserDecryptionKey(mediumSecretMkgUserKeyUid)
// retrieve the key
PrivateKey originalMediumSecretMkgUserKey =
    client.retrieveUserDecryptionKey(mediumSecretMkgUserKeyUid);

// Now rotate the MKG attribute - all active keys will be rekeyed
client.rotateAttributes(
    privateMasterKeyUID,
    new Attr[] { new Attr("Department", "MKG") }
);

// retrieve the rekeyed public key
PrivateKey rekeyedPublicKey =
    client.retrievePublicMasterKey(publicKeyUID);
// retrieve the rekeyed user decryption key
PrivateKey rekeyedMediumSecretMkgUserKey =
    client.retrieveUserDecryptionKey(mediumSecretMkgUserKeyUid);

Coming soon!

creating a new medium secret marketing message

const { CoverCryptHybridEncryption } = await CoverCrypt();

const mediumSecretMkgData = new TextEncoder().encode(
  "medium_secret_mkg_message"
)
encrypter = new CoverCryptHybridEncryption(policy, rekeyedPublicKey)
const newMediumSecretMkgCiphertext = encrypter.encrypt(
  "Department::MKG && Security Level::Medium Secret",
  mediumSecretMkgData
)
byte[] mediumSecretMkgData = "medium_secret_mkg_message".getBytes(StandardCharsets.UTF_8);
Attr[] mediumSecretMkgEncryptionPolicy = "Department::MKG && Security Level::Medium Secret")
};
byte[] mediumSecretMkgCT = ffi.encrypt(
    policy, rekeyedPublicKey.bytes(), 
    mediumSecretMkgEncryptionPolicy, mediumSecretMkgData
);

Coming soon!

decrypting the messages with the rekeyed key

The automatically rekeyed medium secret marketing user key can still decrypt the “old” low secret marketing message, as well as the new medium secret marketing message.

const { CoverCryptHybridDecryption } = await CoverCrypt();

// lowSecretMkgCiphertext
const oldMediumSecretMkgCleartext = new CoverCryptHybridDecryption(
  rekeyedMediumSecretMkgUserKey
).decrypt(lowSecretMkgCiphertext)
assert(lowSecretMkgData, oldMediumSecretMkgCleartext)

// newMediumSecretMkgCiphertext
const newMediumSecretMkgCleartext = new CoverCryptHybridDecryption(
  rekeyedMediumSecretMkgUserKey
).decrypt(newMediumSecretMkgCiphertext)
assert(mediumSecretMkgData, newMediumSecretMkgCleartext)
// lowSecretMkgCT
byte[] data = ffi.decrypt(
    rekeyedMediumSecretMkgUserKey.bytes(), 
    lowSecretMkgCT
);
assertArrayEquals(
    lowSecretMkgData,
    data
);

// mediumSecretMkgCT
byte[] data = ffi.decrypt(
    rekeyedMediumSecretMkgUserKey.bytes(), 
    mediumSecretMkgCT
);
assertArrayEquals(
    mediumSecretMkgData,
    data
);

Coming soon!

decrypting the messages with the NON rekeyed key

However, the old, non rekeyed medium secret marketing user key can still decrypt the old low secret marketing message but not the new medium secret marketing message:

const { CoverCryptHybridDecryption } = await CoverCrypt();

// lowSecretMkgCiphertext
const plaintext_ = new CoverCryptHybridDecryption(
  originalMediumSecretMkgUserKey
).decrypt(lowSecretMkgCiphertext)
assert(lowSecretMkgData, plaintext_)

// newMediumSecretMkgCiphertext
try {
  // will throw
  new CoverCryptHybridDecryption(originalMediumSecretMkgUserKey).decrypt(
    newMediumSecretMkgCiphertext
  )
} catch (error) {
  // ==> the non rekeyed key cannot decrypt the new message after rotation
}
// lowSecretMkgCT
byte[] data = ffi.decrypt(
    originalMediumSecretMkgUserKey.bytes(), 
    lowSecretMkgCT
);
assertArrayEquals(
    lowSecretMkgData,
    data
);

// mediumSecretMkgCT
try {
    // will throw
    ffi.decrypt(
        originalMediumSecretMkgUserKey.bytes(), 
        mediumSecretMkgCT
    );
} catch (CosmianException e) {
    // ==> the non rekeyed key cannot decrypt the new message after rotation
}

Coming soon!

All code

    import { CoverCrypt, KmipClient, Policy, PolicyAxis } from "cloudproof_js"

    const assert = (x, y) => {
    if (new TextDecoder().decode(x) !== new TextDecoder().decode(y))
        throw new Error("Items MUST be equal (left: " + x + " right: " + y + ")")
    }

    //
    // Creating a policy
    //
    const policy = new Policy([
    new PolicyAxis("Department", ["R&D", "HR", "FIN", "MKG"], false),
    new PolicyAxis(
        "Security Level",
        ["Protected", "Low Secret", "Medium Secret", "High Secret", "Top Secret"],
        true
    )
    ])

    ;(async () => {
    const client = new KmipClient(new URL("http://localhost:9998/kmip/2_1"))

    //
    // Generating the master keys
    //
    // create master keys
    const [privateMasterKeyUID, publicKeyUID] =
        await client.createAbeMasterKeyPair(policy)

    // fetch the keys from the KMS
    const privateMasterKey = await client.retrieveAbePrivateMasterKey(
        privateMasterKeyUID
    )
    // eslint-disable-next-line no-unused-vars
    const privateMasterKeyBytes = privateMasterKey.bytes()
    const publicKey = await client.retrieveAbePublicMasterKey(publicKeyUID)
    const publicKeyBytes = publicKey.bytes()

    //
    // Encrypting Data
    //
    const { CoverCryptHybridDecryption, CoverCryptHybridEncryption } =
        await CoverCrypt()

    // a low secret marketing message
    const lowSecretMkgData = new TextEncoder().encode("low_secret_mkg_message")
    // The constructor also accepts the public key object returned by the KMS
    let encrypter = new CoverCryptHybridEncryption(policy, publicKeyBytes)
    const lowSecretMkgCiphertext = encrypter.encrypt(
        "Department::MKG && Security Level::Low Secret",
        lowSecretMkgData
    )

    // a top secret marketing message
    const topSecretMkgData = new TextEncoder().encode("top_secret_mkg_message")
    encrypter = new CoverCryptHybridEncryption(policy, publicKeyBytes)
    const topSecretMkgCiphertext = encrypter.encrypt(
        "Department::MKG && Security Level::Top Secret",
        topSecretMkgData
    )

    // a low secret finance message
    const lowSecretFinData = new TextEncoder().encode("low_secret_fin_message")
    encrypter = new CoverCryptHybridEncryption(policy, publicKeyBytes)
    const lowSecretFinCiphertext = encrypter.encrypt(
        "Department::FIN && Security Level::Low Secret",
        lowSecretFinData
    )

    //
    // Generating User Decryption Keys
    //
    // the medium secret marketing user
    const mediumSecretMkgAccess =
        "Department::MKG && Security Level::Medium Secret"
    const mediumSecretMkgUserKeyUid = await client.createAbeUserDecryptionKey(
        mediumSecretMkgAccess,
        privateMasterKeyUID
    )
    const mediumSecretMkgUserKey = await client.retrieveAbeUserDecryptionKey(
        mediumSecretMkgUserKeyUid
    )
    const mediumSecretMkgUserKeyBytes = mediumSecretMkgUserKey.bytes()

    // the top secret marketing financial user
    const topSecretMkgFinAccess =
        "(Department::MKG || Department::FIN) && Security Level::Top Secret"
    const topSecretMkgFinUserKeyUid = await client.createAbeUserDecryptionKey(
        topSecretMkgFinAccess,
        privateMasterKeyUID
    )
    const topSecretMkgFinUserKey = await client.retrieveAbeUserDecryptionKey(
        topSecretMkgFinUserKeyUid
    )
    const topSecretMkgFinUserKeyBytes = topSecretMkgFinUserKey.bytes()

    // the top secret financial user
    const topSecretFinAccess = "(Department::FIN) && Security Level::Top Secret"
    const topSecretFinUserKeyUid = await client.createAbeUserDecryptionKey(
        topSecretFinAccess,
        privateMasterKeyUID
    )
    const topSecretFinUserKey = await client.retrieveAbeUserDecryptionKey(
        topSecretFinUserKeyUid
    )
    // eslint-disable-next-line no-unused-vars
    const topSecretFinUserKeyBytes = topSecretFinUserKey.bytes()

    //
    // Decrypting Ciphertexts
    //
    //  note: the constructor also accepts the private key object returned by the KMS
    const lowSecretMkgCleartext = new CoverCryptHybridDecryption(
        mediumSecretMkgUserKeyBytes
    ).decrypt(lowSecretMkgCiphertext)
    assert(lowSecretMkgCleartext, lowSecretMkgData)

    // .. however it can neither decrypt a marketing message with higher security:
    try {
        // will throw
        new CoverCryptHybridDecryption(mediumSecretMkgUserKey).decrypt(
        topSecretMkgCiphertext
        )
    } catch (error) {
        // ==> the user is not be able to decrypt
    }

    // ... nor decrypt a message from another department even with a lower security:
    try {
        // will throw
        new CoverCryptHybridDecryption(topSecretFinUserKey).decrypt(
        lowSecretMkgCiphertext
        )
    } catch (error) {
        // ==> the user is not be able to decrypt
    }

    // lowSecretMkgCiphertext
    const lowSecretMkgCleartext2 = new CoverCryptHybridDecryption(
        topSecretMkgFinUserKeyBytes
    ).decrypt(lowSecretMkgCiphertext)
    assert(lowSecretMkgData, lowSecretMkgCleartext2)

    // lowSecretFinCiphertext
    const topSecretMkgCleartext = new CoverCryptHybridDecryption(
        topSecretMkgFinUserKeyBytes
    ).decrypt(topSecretMkgCiphertext)
    assert(topSecretMkgData, topSecretMkgCleartext)

    // lowSecretFinCiphertext
    const lowSecretFinCleartext = new CoverCryptHybridDecryption(
        topSecretMkgFinUserKeyBytes
    ).decrypt(lowSecretFinCiphertext)
    assert(lowSecretFinData, lowSecretFinCleartext)

    //
    // Rotating Attributes
    //
    // retrieve the key
    const originalMediumSecretMkgUserKey =
        await client.retrieveAbeUserDecryptionKey(mediumSecretMkgUserKeyUid)

    // Now revoke the MKG attribute - all active keys will be rekeyed
    client.rotateAbeAttributes(privateMasterKeyUID, ["Department::MKG"])

    // retrieve the rekeyed public key
    const rekeyedPublicKey = await client.retrieveAbePublicMasterKey(publicKeyUID)
    // retrieve the rekeyed user decryption key
    const rekeyedMediumSecretMkgUserKey =
        await client.retrieveAbeUserDecryptionKey(mediumSecretMkgUserKeyUid)

    //
    // creating a new medium secret marketing message
    //
    const mediumSecretMkgData = new TextEncoder().encode(
        "medium_secret_mkg_message"
    )
    encrypter = new CoverCryptHybridEncryption(policy, rekeyedPublicKey)
    const newMediumSecretMkgCiphertext = encrypter.encrypt(
        "Department::MKG && Security Level::Medium Secret",
        mediumSecretMkgData
    )

    //
    // decrypting the messages with the rekeyed key
    //
    // lowSecretMkgCiphertext
    const oldMediumSecretMkgCleartext = new CoverCryptHybridDecryption(
        rekeyedMediumSecretMkgUserKey
    ).decrypt(lowSecretMkgCiphertext)
    assert(lowSecretMkgData, oldMediumSecretMkgCleartext)

    // newMediumSecretMkgCiphertext
    const newMediumSecretMkgCleartext = new CoverCryptHybridDecryption(
        rekeyedMediumSecretMkgUserKey
    ).decrypt(newMediumSecretMkgCiphertext)
    assert(mediumSecretMkgData, newMediumSecretMkgCleartext)

    //
    // decrypting the messages with the NON rekeyed key
    //
    // lowSecretMkgCiphertext
    const plaintext_ = new CoverCryptHybridDecryption(
        originalMediumSecretMkgUserKey
    ).decrypt(lowSecretMkgCiphertext)
    assert(lowSecretMkgData, plaintext_)

    // newMediumSecretMkgCiphertext
    try {
        // will throw
        new CoverCryptHybridDecryption(originalMediumSecretMkgUserKey).decrypt(
        newMediumSecretMkgCiphertext
        )
    } catch (error) {
        // ==> the non rekeyed key cannot decrypt new message after rotation
    }

    console.log("Succeeded!")
    })()

© Copyright 2018-2022 Cosmian. All rights reserved