Skip to content

ABE Benchmarking Tutorial

Raphael H edited this page Jan 29, 2020 · 5 revisions

ABE Benchmark Tutorial

About this Tutorial

This tutorial will teach you how the ABE Benchmarks work, and how you can create benchmarks for your own ABE schemes implemented using the upb.crypto libraries. Specifically, any scheme which wants to use this benchmark must implement the PredicateEncryptionScheme interface from the CRACO library.

The tutorial will first give an overview of the capabilities and java classes involved. It will then guide you through creating an example benchmark for the Large Universe CP-ABE scheme of Waters, 2011. The scheme itself is included in the upb.crypto.craco library.

An Overview

The ABE benchmarking code is contained in the test/java folder of the project, in the de.upb.crypto.craco.abe package. Here, you can see a number of existing benchmarks for different ABE schemes included in the CRACO library, as well as a generic package.

The generic package includes the java classes used by the benchmarks. Let's take a look at what each of them do.

ABEBenchmark

This includes the benchmarking code itself. Its constructor takes a ABEBenchmarkConfig and ABEBenchmarkParams instances which are used to configure the benchmark. To run the benchmark, the doBenchmark() method is used. It will repeatedly run setup, key generation, and encryption and decryption and measure their execution times. At the end, averages will be printed.

ABEBenchmarkConfig

This class is mostly for (theoretically) scheme-independent configuration of the benchmark. Here you can set things like number of warmup runs, the number of different setups, the number of key generations per setup, and number of encryptions/decryptions per key generation. Furthermore, you can configure whether this benchmark is for a ciphertext-policy or key-policy scheme.

The printDetails attribute determines whether execution times are printed during the benchmark. If set to false, only average executiom times will be printed in the summary after the benchmark.

Lastly, you can specify pairs of attributes/policies. The benchmark will run a benchmark per such pair and later split up the result per pair. This way you can test different number of attributes.

ABEBenchmarkConfigBuilder

Since the ABEBenchmarkConfig class has a lot of attributes, a builder pattern is used to build ABEBechmarkConfig instances. Instead of instantiating ABEBenchmarkConfig objects directly, you can use this class to build up such an object, and use the buildConfig() method to obtain an instantiation with your previously selected settings (using the setX() methods).

SetOfAttributesPolicyNameTriple

The attribute/policy pairs are specified using instances of this class. Additionally, a name can be given to such a pair, this will make it easier to distinguish the benchmark results later.

ABEBenchmarkParams

The benchmark code requires the scheme to implement the PredicateEncryptionScheme interface. This interface does not provide methods for public parameter/master secret setup. Furthermore, the benchmark needs plaintexts to benchmark encryption/decryption.

Hence, this (abstract) class is used to specify how to perform the setup using the doSetup() method. doSetup() takes as argument a KeyIndex and CiphertextIndex object. This is in case the scheme needs access to the attributes/policy for setup. Whether KeyIndex or CiphertextIndex contains attributes or policy depends on the type of scheme set by the isCPABE attribute set in the ABEBenchmarkConfig used by the ABEBenchmark.

generatePlainText supplies the benchmark with fresh plaintexts for benchmarking encryption/decryption.

To create your own benchmark, you will need to extend this class and supply the aforementioned methods.

ABEWat11 Example

To create a benchmark for any scheme, we first need to create a subclass of ABEBenchmarkParams. Create a new class file in the de.upb.crypto.craco.abe package, called ABEWat11BenchmarkParams and make it extend ABEBenchmarkParams.

Next, we need to implement the abstract doSetup() method.

 @Override
public void doSetup(KeyIndex kIndex, CiphertextIndex cIndex) {
    ABECPWat11Setup cpSetup = new ABECPWat11Setup();
    cpSetup.doKeyGenRandomOracle(60);
    ABECPWat11PublicParameters pp = cpSetup.getPublicParameters();
    this.setScheme(new ABECPWat11(pp));
    this.setMasterSecret(cpSetup.getMasterSecret());
    this.setPublicParameters(pp);
}

As you can see, we run setup of the scheme in the first two lines of the method body. Then we retrieve the public parameters and set the scheme, masterSecret and publicParameters attributes of our object. Setting publicParameters is not necessary for the benchmark; however, it will be necessary for generating new plaintexts. Setting scheme and masterSecret is necessary, as the benchmark cannot run without them.

Furthermore, we did not use the method arguments of doSetup. This scheme does not require knowledge of the attributes/policy for setup. Other schemes may differ in this regard, for example the small universe CP-ABE construction by Waters, 2011.

Lastly, we need to add the generatePlainText method. We simply choose a random plaintext element. Here, we need the public parameters to get the target group.

@Override
public PlainText generatePlainText() {
        return new GroupElementPlainText(
                ((ABECPWat11PublicParameters) this.getPublicParameters()).getGroupGT().getUniformlyRandomNonNeutral()
        );
}

The finished class looks as follows (not including imports or package declaration).

public class ABEWat11BenchmarkParams extends ABEBenchmarkParams {

    @Override
    public void doSetup(KeyIndex kIndex, CiphertextIndex cIndex) {
        ABECPWat11Setup cpSetup = new ABECPWat11Setup();
        cpSetup.doKeyGenRandomOracle(60);
        ABECPWat11PublicParameters pp = cpSetup.getPublicParameters();
        this.setScheme(new ABECPWat11(pp));
        this.setMasterSecret(cpSetup.getMasterSecret());
        this.setPublicParameters(pp);
    }

    @Override
    public PlainText generatePlainText() {
        return new GroupElementPlainText(
                ((ABECPWat11PublicParameters) this.getPublicParameters()).getGroupGT().getUniformlyRandomNonNeutral()
        );
    }
}

Next we need to create some code we can run to call the benchmark and construct the ABEBenchmarkConfig object. To do this, we create an ABEWat11Benchmark class with a main method we can run. We can already instantiate our params we just created.

public class ABEWat11Benchmark {

    public static void main(String[] args) {
                ABEBenchmarkParams params = new ABEWat11BenchmarkParams();

    }
}

To run the benchmark code, we also need a ABEBenchmarkConfig object. To instantiate this, we make use of the ABEBenchmarkConfigBuilder. To starto ff, we just want a simple configuration, with default values.

public static void main(String[] args) {
    ABEBenchmarkParams params = new ABEWat11BenchmarkParams();
    ABEBenchmarkConfig config = new ABEBenchmarkConfigBuilder().setIsCPABE(true).buildConfig();
}

The setIsCPABe is the only required call to ABEBenchmarkConfigBuilder since otherwise the benchmark does not know whether we are testing a CP-ABE and KP-ABE. Since the scheme we are benchmarking is a CP-ABE scheme, we set it to true. The rest of the values will be set to default.

Now, the only thing that is missing is running the benchmark itself.

public static void main(String[] args) {
    ABEBenchmarkParams params = new ABEWat11BenchmarkParams();
    ABEBenchmarkConfig config = new ABEBenchmarkConfigBuilder().setIsCPABE(true).buildConfig();
    ABEBenchmark benchmark = new ABEBenchmark(params, config);
    benchmark.doBenchmark();
}

Run the main method. It will take a couple seconds. During the benchmarking, execution times and further information about the current benchmark runs will be printed to the console. After the benchmark, a summary with average execution times will be printed for each attribute/policy pair. As you can see, the default attribute/policy selection consists of attribute sizes from 2 to 128 (growing exponentially) and boolean policies consisting of exclusively AND and exclusively OR gates.

Further Configuration

So far, our configuration of the benchmark is quite meager. Let's expand it. To explain the different options, we will have to take a look at how the benchmark works.

This can be seen by looking at the console output during the benchmark.

-- Selected attribute/policy '128 BigInt ALL AND gates' --
	-- Benchmark Run 0 --
		Setup: Total execution time: 134ms
		-- Performing Key Generation Number 0 --
			EncryptionKey Generation: Total execution time: 0ms
			DecryptionKey Generation: Total execution time: 6279ms
			-- Performing Encrypt/Decrypt Cycle 0 --
				Encryption: Total execution time: 3925ms
				Decryption: Total execution time: 2514ms
				Encryption/Decryption correct: true
-- Selected attribute/policy '128 BigInt ALL OR gates' --
	-- Benchmark Run 0 --
		Setup: Total execution time: 130ms
		-- Performing Key Generation Number 0 --
			EncryptionKey Generation: Total execution time: 0ms
			DecryptionKey Generation: Total execution time: 6060ms
			-- Performing Encrypt/Decrypt Cycle 0 --
				Encryption: Total execution time: 4027ms
				Decryption: Total execution time: 105ms
				Encryption/Decryption correct: true

As you can see, for each attribute/policy pair, a number of benchmark runs are performed. This number is determined by the numSetup attribute of the ABEBenchmarkConfig object used. For each benchmark run, the scheme is newly setup using our previously defined doSetup() method.

For each setup, a number of key generations are performed, the number of which is determined by the numKeyGenerations attribute.

Lastly, for each key generation, a plaintext is generated using the generatePlainText() method, encrypted, and then decrypted again. We also test whether the decryption succeeded as a correctness check. The number of such plaintext-generation/encryption/decryption cycles is determined by the encDecCycles attribute.

So, lets reconfigure our benchmark.

public static void main(String[] args) {
    ABEBenchmarkParams params = new ABEWat11BenchmarkParams();
    ABEBenchmarkConfigBuilder configBuilder = new ABEBenchmarkConfigBuilder().setIsCPABE(true);
    configBuilder.setNumSetups(2);
    configBuilder.setNumKeyGenerations(2);
    configBuilder.setNumEncDecCycles(2);
    ABEBenchmarkConfig config = configBuilder.buildConfig();
    ABEBenchmark benchmark = new ABEBenchmark(params, config);
    benchmark.doBenchmark();
}

Since we have increased the number of runs, the benchmark should now take longer.

Of course, you may not want to use the default selection of attribute/policy pairs. The ABEBenchmarkConfigBuilder object's setSetOfAttributesPolcyNameTriples() method accepts an array of name/attributes/policy triples which will then be used in the benchmark instead of the default selection.

You can also change the number of warmup runs. A warmup run just simply does one run through each of setup, key generation, and one encryption/decryption cycle.

If you don't want your console to be cluttered during the benchmark, you can also disable most of the printing by using setPrintDetails(false).

Choosing Pairing

Depending on the scheme, unless you specify a specific bilinear group, the setup algorithm will probably make use of the bilinear group selection of the upb.crypto.math library. It will select a pairing of the right type providing the given security level. However, the pairings provided there are quite inefficient. If you want, you can instead use our wrapper around the efficient BN264 pairing from the mcl library. Keep in mind that this is a type 3 pairing.