Skip to content

Commit eea2f15

Browse files
authored
Merge pull request #1 from Cosmian/feature/native_lib
Add support for local hybrid encryption ABE + AES
2 parents be470dc + 6ddf004 commit eea2f15

32 files changed

+1406
-137
lines changed

.classpath

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
<attribute name="test" value="true"/>
3838
</attributes>
3939
</classpathentry>
40+
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
41+
<attributes>
42+
<attribute name="maven.pomderived" value="true"/>
43+
</attributes>
44+
</classpathentry>
4045
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
4146
<attributes>
4247
<attribute name="optional" value="true"/>

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
target/
2-
dependency-reduced-pom.xml
2+
dependency-reduced-pom.xml
3+
hs_err_*.log
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
eclipse.preferences.version=1
22
encoding//src/main/java=UTF-8
3+
encoding//src/main/resources=UTF-8
34
encoding//src/test/java=UTF-8
45
encoding//src/test/resources=UTF-8
56
encoding/<project>=UTF-8

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ The library provides a Java friendly API to the Cosmian Ubiquitous Encryption pl
1111
:warning: This is an early release of the java library for Cosmian Ubiquitous Encryption. Only a limited set of the operations is publicly supported.
1212

1313

14+
- [Confidential Data Access](#confidential-data-access)
15+
- [Quick Start](#quick-start)
16+
- [Local encryption and decryption](#local-encryption-and-decryption)
17+
- [Confidential Micro Services](#confidential-micro-services)
18+
- [Confidential KMS](#confidential-kms)
19+
20+
21+
1422
## Confidential Data Access
1523

1624
Cosmian Ubiquitous Encryption provides the ability to encrypt data - locally or inside the KMS - using policy attributes. The only users able to decrypt the data are those possessing a key holding the correct access policy.
@@ -32,6 +40,15 @@ Data is encrypted with two values, one from each axis, say: `[MKG, Low Secret]`
3240

3341
A user is able to decrypt data only if it possesses a key with an access policy with sufficient security level and the code for the department, say ` Top Secret && ( MKG || FIN )`
3442

43+
44+
### Local encryption and decryption
45+
46+
In addition to KMS encryption and decryption, the library offers the ability to perform and hybrid encryption ABE+AES. This requires having the native dynamic library [abe_gpsw](https://github.com/Cosmian/abe_gpsw) deployed on your system.
47+
48+
The native library can also be packaged as part of the jar package by placing a copy in the `src/main/resources/{OS}-{ARCH}` folder (`linux-amd64` for linux) and running `mvn package`.
49+
50+
To learn how to use the local ABE+AES hybrid encryption facilities, check [the Hybrid ABE AES tests](src/test/java/com/cosmian/TestLocalABE_AES.java)
51+
3552
## Confidential Micro Services
3653

3754
*Not publicly available yet. Call Cosmian for early access*
@@ -59,4 +76,4 @@ Use of Cosmian KMS is included with the services above.
5976

6077
The KMS offers a KMIP 2.1 interface.
6178

62-
*only the KMS operations required to enable the Confidential Data Access and Confidential Micro Services are publicly available for now. Contact Cosmian for full KMS access*
79+
*Only the KMS operations required to enable the Confidential Data Access and Confidential Micro Services are publicly available for now. Contact Cosmian for full KMS access*

pom.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>com.cosmian</groupId>
44
<artifactId>cosmian_java_lib</artifactId>
5-
<version>0.2.1-SNAPSHOT</version>
5+
<version>0.3.0</version>
66
<properties>
77
<maven.compiler.source>1.8</maven.compiler.source>
88
<maven.compiler.target>1.8</maven.compiler.target>
99
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1010
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
11+
<jna.version>5.10.0</jna.version>
1112
</properties>
1213
<dependencies>
1314
<dependency>
@@ -30,6 +31,18 @@
3031
<artifactId>classmate</artifactId>
3132
<version>1.5.0</version>
3233
</dependency>
34+
<dependency>
35+
<!-- JNA dependency -->
36+
<groupId>net.java.dev.jna</groupId>
37+
<artifactId>jna</artifactId>
38+
<version>${jna.version}</version>
39+
</dependency>
40+
<dependency>
41+
<!-- JNA platform dependency -->
42+
<groupId>net.java.dev.jna</groupId>
43+
<artifactId>jna-platform</artifactId>
44+
<version>${jna.version}</version>
45+
</dependency>
3346
<dependency>
3447
<groupId>org.junit.jupiter</groupId>
3548
<artifactId>junit-jupiter-engine</artifactId>
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
package com.cosmian.jna;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.util.ArrayList;
5+
import java.util.Arrays;
6+
7+
import com.cosmian.jna.abe.DecryptedHeader;
8+
import com.cosmian.jna.abe.EncryptedHeader;
9+
import com.cosmian.jna.abe.FfiWrapper;
10+
import com.cosmian.rest.abe.acccess_policy.Attr;
11+
import com.cosmian.rest.abe.policy.Policy;
12+
import com.fasterxml.jackson.core.JsonProcessingException;
13+
import com.fasterxml.jackson.databind.ObjectMapper;
14+
import com.sun.jna.Memory;
15+
import com.sun.jna.Pointer;
16+
import com.sun.jna.ptr.IntByReference;
17+
18+
public class Ffi {
19+
20+
/**
21+
* Return the last error in a String that does not exceed 1023 bytes
22+
*/
23+
public String get_last_error() throws FfiException {
24+
return get_last_error(1023);
25+
}
26+
27+
/**
28+
* Return the last error in a String that does not exceed `max_len` bytes
29+
*/
30+
public String get_last_error(int max_len) throws FfiException {
31+
if (max_len < 1) {
32+
throw new FfiException("get_last_error: max_lem must be at least one");
33+
}
34+
byte[] output = new byte[max_len + 1];
35+
IntByReference outputSize = new IntByReference(output.length);
36+
if (FfiWrapper.INSTANCE.get_last_error(output, outputSize) == 0) {
37+
return new String(Arrays.copyOfRange(output, 0, outputSize.getValue()), StandardCharsets.UTF_8);
38+
}
39+
throw new FfiException("Failed retrieving the last error; check the debug logs");
40+
}
41+
42+
/**
43+
* Set the last error on the native lib
44+
*
45+
* @param error_msg
46+
* @throws FfiException
47+
*/
48+
public void set_error(String error_msg) throws FfiException {
49+
unwrap(FfiWrapper.INSTANCE.set_error(error_msg));
50+
}
51+
52+
/**
53+
* Generate an hybrid encryption header.
54+
*
55+
* A symmetric key is randomly generated and encrypted using the ABE schemes and
56+
* the provided policy attributes for the given policy
57+
* .
58+
* If provided, the resource `uid` and the `additionalData` are symmetrically
59+
* encrypted and appended to the encrypted header.
60+
*
61+
* @param policy the policy to use
62+
* @param publicKey the ABE public key
63+
* @param attributes the policy attributes used to encrypt the generated
64+
* symmetric key
65+
* @param uid the optional resource uid
66+
* @param additionalData optional additional data
67+
* @return the encrypted header, bytes and symmetric key
68+
* @throws FfiException
69+
*/
70+
public EncryptedHeader encryptHeader(Policy policy, byte[] publicKey, Attr[] attributes, byte[] uid,
71+
byte[] additionalData) throws FfiException {
72+
73+
// For the JSON strings
74+
ObjectMapper mapper = new ObjectMapper();
75+
76+
// Symmetric Key OUT
77+
byte[] symmetricKeyBuffer = new byte[1024];
78+
IntByReference symmetricKeyBufferSize = new IntByReference(symmetricKeyBuffer.length);
79+
80+
// Header Bytes OUT
81+
byte[] headerBytesBuffer = new byte[8192 + uid.length + additionalData.length];
82+
IntByReference headerBytesBufferSize = new IntByReference(headerBytesBuffer.length);
83+
84+
// Policy
85+
String policyJson;
86+
try {
87+
policyJson = mapper.writeValueAsString(policy);
88+
} catch (JsonProcessingException e) {
89+
throw new FfiException("Invalid Policy");
90+
}
91+
System.out.println("JAVA POLICY: " + policyJson);
92+
93+
// Public Key
94+
final Pointer publicKeyPointer = new Memory(publicKey.length);
95+
publicKeyPointer.write(0, publicKey, 0, publicKey.length);
96+
97+
// Attributes
98+
// The value must be the JSON array of the String representation of the Attrs
99+
ArrayList<String> attributesArray = new ArrayList<String>();
100+
for (Attr attr : attributes) {
101+
attributesArray.add(attr.toString());
102+
}
103+
String attributesJson;
104+
try {
105+
attributesJson = mapper.writeValueAsString(attributesArray);
106+
} catch (JsonProcessingException e) {
107+
throw new FfiException("Invalid Policy");
108+
}
109+
System.out.println("JAVA ATTRIBUTES: " + attributesJson);
110+
111+
// Uid
112+
final Pointer uidPointer;
113+
if (uid.length == 0) {
114+
uidPointer = Pointer.NULL;
115+
} else {
116+
uidPointer = new Memory(uid.length);
117+
uidPointer.write(0, uid, 0, uid.length);
118+
}
119+
120+
// Additional Data
121+
final Pointer additionalDataPointer;
122+
if (additionalData.length == 0) {
123+
additionalDataPointer = Pointer.NULL;
124+
} else {
125+
additionalDataPointer = new Memory(additionalData.length);
126+
additionalDataPointer.write(0, additionalData, 0, additionalData.length);
127+
}
128+
129+
unwrap(FfiWrapper.INSTANCE.h_aes_encrypt_header(symmetricKeyBuffer, symmetricKeyBufferSize, headerBytesBuffer,
130+
headerBytesBufferSize, policyJson, publicKeyPointer,
131+
publicKey.length,
132+
attributesJson, uidPointer, uid.length, additionalDataPointer, additionalData.length));
133+
134+
return new EncryptedHeader(Arrays.copyOfRange(symmetricKeyBuffer, 0, symmetricKeyBufferSize.getValue()),
135+
Arrays.copyOfRange(headerBytesBuffer, 0, headerBytesBufferSize.getValue()));
136+
}
137+
138+
/**
139+
* Decrypt a hybrid header, recovering the symmetric key, and optionally, the
140+
* resource UID and additional data
141+
*
142+
* @param userDecryptionKey the ABE user decryption key
143+
* @param encryptedHeaderBytes the encrypted header
144+
* @param uidLen the bytes length of the expected UID
145+
* @param additionalDataLen the bytes length of the expected additional data
146+
* @return The decrypted header: symmetric key, uid and additional data
147+
* @throws FfiException
148+
*/
149+
public DecryptedHeader decryptHeader(byte[] userDecryptionKey, byte[] encryptedHeaderBytes, int uidLen,
150+
int additionalDataLen) throws FfiException {
151+
152+
// Symmetric Key OUT
153+
byte[] symmetricKeyBuffer = new byte[1024];
154+
IntByReference symmetricKeyBufferSize = new IntByReference(symmetricKeyBuffer.length);
155+
156+
// UID OUT
157+
byte[] uidBuffer = new byte[uidLen];
158+
IntByReference uidBufferSize = new IntByReference(uidBuffer.length);
159+
160+
// Additional Data OUT
161+
byte[] additionalDataBuffer = new byte[additionalDataLen];
162+
IntByReference additionalDataBufferSize = new IntByReference(additionalDataBuffer.length);
163+
164+
// User Decryption Key
165+
final Pointer userDecryptionKeyPointer = new Memory(userDecryptionKey.length);
166+
userDecryptionKeyPointer.write(0, userDecryptionKey, 0, userDecryptionKey.length);
167+
System.out.println("Ffi.decryptHeader() udk len " + userDecryptionKey.length);
168+
169+
// encrypted bytes
170+
final Pointer encryptedHeaderBytesPointer = new Memory(encryptedHeaderBytes.length);
171+
encryptedHeaderBytesPointer.write(0, encryptedHeaderBytes, 0, encryptedHeaderBytes.length);
172+
System.out.println("Ffi.decryptHeader() encrypted header len " + encryptedHeaderBytes.length);
173+
174+
unwrap(FfiWrapper.INSTANCE.h_aes_decrypt_header(symmetricKeyBuffer, symmetricKeyBufferSize, uidBuffer,
175+
uidBufferSize, additionalDataBuffer, additionalDataBufferSize, encryptedHeaderBytesPointer,
176+
encryptedHeaderBytes.length, userDecryptionKeyPointer,
177+
userDecryptionKey.length));
178+
179+
return new DecryptedHeader(
180+
Arrays.copyOfRange(symmetricKeyBuffer, 0, symmetricKeyBufferSize.getValue()),
181+
Arrays.copyOfRange(uidBuffer, 0, uidBufferSize.getValue()),
182+
Arrays.copyOfRange(additionalDataBuffer, 0, additionalDataBufferSize.getValue()));
183+
}
184+
185+
/**
186+
* The overhead in bytes (over the clear text) generated by the symmetric
187+
* encryption scheme (AES 256 GCM)
188+
*
189+
* @return the overhead bytes
190+
*/
191+
public int symmetricEncryptionOverhead() {
192+
return FfiWrapper.INSTANCE.h_aes_symmetric_encryption_overhead();
193+
}
194+
195+
/**
196+
* Symmetrically encrypt a block of clear text data.
197+
*
198+
* The UID and Block Number are part of the AEAD of the symmetric scheme.
199+
*
200+
* @param symmetricKey The key to use to symmetrically encrypt the block
201+
* @param uid The resource UID
202+
* @param blockNumber the block number when the resource is split in multiple
203+
* blocks
204+
* @param clearText the clear text to encrypt
205+
* @return the encrypted block
206+
* @throws FfiException
207+
*/
208+
public byte[] encryptBlock(byte[] symmetricKey, byte[] uid, int blockNumber,
209+
byte[] clearText) throws FfiException {
210+
211+
// Header Bytes OUT
212+
byte[] cipherTextBuffer = new byte[FfiWrapper.INSTANCE.h_aes_symmetric_encryption_overhead()
213+
+ clearText.length];
214+
IntByReference cipherTextBufferSize = new IntByReference(cipherTextBuffer.length);
215+
216+
// Symmetric Key
217+
final Pointer symmetricKeyPointer = new Memory(symmetricKey.length);
218+
symmetricKeyPointer.write(0, symmetricKey, 0, symmetricKey.length);
219+
220+
// Uid
221+
final Pointer uidPointer = new Memory(uid.length);
222+
uidPointer.write(0, uid, 0, uid.length);
223+
224+
// Additional Data
225+
final Pointer dataPointer = new Memory(clearText.length);
226+
dataPointer.write(0, clearText, 0, clearText.length);
227+
228+
unwrap(FfiWrapper.INSTANCE.h_aes_encrypt_block(cipherTextBuffer,
229+
cipherTextBufferSize, symmetricKeyPointer,
230+
symmetricKey.length,
231+
uidPointer, uid.length, blockNumber, dataPointer, clearText.length));
232+
233+
return Arrays.copyOfRange(cipherTextBuffer, 0, cipherTextBufferSize.getValue());
234+
}
235+
236+
/**
237+
* Symmetrically decrypt a block of encrypted data.
238+
*
239+
* The resource UID and block Number must match those supplied on encryption or
240+
* decryption will fail.
241+
*
242+
* @param symmetricKey the symmetric key to use
243+
* @param uid the resource UID
244+
* @param blockNumber the block number of the resource
245+
* @param encryptedBytes the encrypted block bytes
246+
* @return the clear text bytes
247+
* @throws FfiException
248+
*/
249+
public byte[] decryptBlock(byte[] symmetricKey, byte[] uid, int blockNumber, byte[] encryptedBytes)
250+
throws FfiException {
251+
252+
// Clear Text Bytes OUT
253+
byte[] clearTextBuffer = new byte[encryptedBytes.length
254+
- FfiWrapper.INSTANCE.h_aes_symmetric_encryption_overhead()];
255+
IntByReference clearTextBufferSize = new IntByReference(clearTextBuffer.length);
256+
257+
// Symmetric Key
258+
final Pointer symmetricKeyPointer = new Memory(symmetricKey.length);
259+
symmetricKeyPointer.write(0, symmetricKey, 0, symmetricKey.length);
260+
261+
// Uid
262+
final Pointer uidPointer = new Memory(uid.length);
263+
uidPointer.write(0, uid, 0, uid.length);
264+
265+
// Encrypted Data
266+
final Pointer encryptedBytesPointer = new Memory(encryptedBytes.length);
267+
encryptedBytesPointer.write(0, encryptedBytes, 0, encryptedBytes.length);
268+
269+
unwrap(FfiWrapper.INSTANCE.h_aes_decrypt_block(clearTextBuffer, clearTextBufferSize, symmetricKeyPointer,
270+
symmetricKey.length, uidPointer, uid.length, blockNumber,
271+
encryptedBytesPointer, encryptedBytes.length));
272+
273+
return Arrays.copyOfRange(clearTextBuffer, 0, clearTextBufferSize.getValue());
274+
}
275+
276+
/**
277+
* If the result of the last FFI call is in Error, recover the last error from
278+
* the native code and throw an exception wrapping it.
279+
*
280+
* @param result the result of the FFI call
281+
* @throws FfiException
282+
*/
283+
private void unwrap(int result) throws FfiException {
284+
if (result == 1) {
285+
throw new FfiException(get_last_error(4095));
286+
}
287+
}
288+
}

0 commit comments

Comments
 (0)