failed"))?;
+ Self::check_java_exceptions(environment)?;
+ Ok(output_vec)
+ }
+
+ /// Checks for any pending Java exceptions in the provided Java environment (`JNIEnv`).
+ /// If one is detected, it is printed to console and cleared so the program doesn't crash.
+ /// # Arguments
+ /// * `environment` - A reference to the Java environment (`JNIEnv`)
+ /// # Returns
+ /// * `Result<(), String>` - A Result type representing either success (if no exceptions
+ /// are found) or an error (if exceptions are found).
+ /// # Errors
+ /// This method may return an error of type `JniError` if:
+ /// * Any pending Java exceptions are found in the provided Java environment.
+ pub fn check_java_exceptions(environment: &JNIEnv) -> Result<(), String> {
+ if environment.exception_check().unwrap_or(true) {
+ let _ = environment.exception_describe();
+ let _ = environment.exception_clear();
+ return Err(String::from("A Java exception occurred, check console for details"));
+ } else {
+ Ok(())
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/tpm/android/knox/java/CryptoManager.java b/src/tpm/android/knox/java/CryptoManager.java
new file mode 100644
index 00000000..dc634038
--- /dev/null
+++ b/src/tpm/android/knox/java/CryptoManager.java
@@ -0,0 +1,449 @@
+package tpm.android.knox.java;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * This class provides all the methods we need for communication with the keystore and cryptographic systems.
+ * It loads the keystore, generates and loads keys, encrypts and decrypts and signs and verifies.
+ *
+ */
+public class CryptoManager {
+ private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
+ private static final int IV_CBC_AND_CTR_AES = 16; // CBC & CTR standard IV size is 16 Byte
+ private static final int IV_GCM_AES = 12; // GCM standard IV size is 12 Byte
+ private static final int IV_CBC_DES = 8; // DES with CBC standard IV size is 8 Byte
+ private static final int TAG_SIZE_GCM = 128; // 128 is the recommended TagSize
+ private final KeyStore keyStore;
+ private String KEY_NAME;
+
+ /**
+ * Constructs a new instance of {@code CryptoManager} with the default Android KeyStore.
+ *
+ * This constructor initializes the {@code CryptoManager} with the default Android KeyStore. The Android KeyStore
+ * provides a secure storage facility for cryptographic keys and certificates. Upon construction, the key store is
+ * initialized, enabling the {@code CryptoManager} to interact with cryptographic keys securely stored on the
+ * Android device. If the initialization of the key store fails, a {@link KeyStoreException} is thrown, indicating
+ * issues with the key store setup process.
+ *
+ * @throws KeyStoreException if the KeyStore Provider does not exist or fails to initialize, indicating issues with
+ * the key store setup process.
+ */
+ public CryptoManager() throws KeyStoreException {
+ keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
+ }
+
+ /**
+ * Generates a new symmetric key and saves it into the Android KeyStore.
+ *
+ * This method initializes a new symmetric key for encryption and decryption purposes using the specified symmetric key algorithm.
+ * The keyGenInfo string is checked for its pattern to ensure that no incorrect values have been transferred.
+ * The key is stored in the Android KeyStore.
+ * Additionally, this method ensures that the key is backed by the strong box feature.
+ *
+ * @param key_id The unique identifier under which the key will be stored in the KeyStore.
+ * @param keyGenInfo A string containing key generation parameters separated by semicolons. Expected format: "KEY_ALGORITHM;KEY_SIZE;BLOCK_MODE;PADDING".
+ * @throws CertificateException if there is an issue loading the certificate chain.
+ * @throws IOException for I/O errors such as incorrect passwords.
+ * @throws NoSuchAlgorithmException if the generation algorithm does not exist or the keystore doesn't exist.
+ * @throws NoSuchProviderException if the provider does not exist.
+ * @throws InvalidAlgorithmParameterException for invalid or nonexistent parameters.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ */
+ public void genKey(String key_id, String keyGenInfo) throws CertificateException,
+ IOException, NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidAlgorithmParameterException, KeyStoreException {
+ Pattern pattern = Pattern.compile("^(AES|DESede);(\\d+);(CBC|GCM|CTR);(NoPadding|PKCS7Padding)$");
+ Matcher matcher = pattern.matcher(keyGenInfo);
+ assert matcher.matches() : "Generate Key keyGenInfo is not valid.";
+
+ String[] keyGenInfoArr = keyGenInfo.split(";");
+ String KEY_ALGORITHM = keyGenInfoArr[0];
+ int KEY_SIZE = Integer.parseInt(keyGenInfoArr[1]);
+ String BLOCKING = keyGenInfoArr[2];
+ String PADDING = keyGenInfoArr[3];
+
+ KEY_NAME = key_id;
+ keyStore.load(null);
+
+ // Check if a key with the given key_id already exists
+ if (keyStore.containsAlias(KEY_NAME)) {
+ throw new KeyStoreException("Key with name " + KEY_NAME + " already exists.");
+ }
+ KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, ANDROID_KEY_STORE);
+ keyGen.init(new KeyGenParameterSpec.Builder(KEY_NAME,
+ KeyProperties.PURPOSE_ENCRYPT |
+ KeyProperties.PURPOSE_DECRYPT)
+ .setKeySize(KEY_SIZE)
+ .setBlockModes(BLOCKING)
+ .setEncryptionPaddings(PADDING)
+ .setIsStrongBoxBacked(true)
+ .build());
+ keyGen.generateKey();
+ }
+
+ /**
+ * Encrypts the given data using a symmetric key stored in the Android KeyStore.
+ *
+ * This method takes plaintext data as input and encrypts it using a symmetric key retrieved from the Android KeyStore.
+ * The encryption process supports GCM, CBC and CTR transformations. A new initialization vector (IV)
+ * is generated and the IV is prepended to the ciphertext. In the case of 3DES only CBC is used, which needs a different IV size than with AES.
+ * The method initializes a {@link Cipher} instance with the appropriate transformation, loads the Android KeyStore, retrieves the symmetric key, and then
+ * initializes the cipher in encryption mode with the retrieved key and the generated IV. Finally, the plaintext data is encrypted
+ * using the cipher's {@code doFinal} method, and the resulting ciphertext is returned as a byte array.
+ *
+ * @param data The plaintext data to be encrypted, represented as a byte array.
+ * @return A byte array representing the encrypted data, with the IV prepended.
+ * @throws NoSuchPaddingException if the requested padding scheme is not available.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws CertificateException if there is an issue loading the certificate chain.
+ * @throws IOException if there is an I/O error during the operation.
+ * @throws InvalidKeyException if the key cannot be cast to a SecretKey.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ * @throws IllegalBlockSizeException if the data length is invalid for the encryption algorithm.
+ * @throws BadPaddingException if the data could not be padded correctly for encryption.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the requested security provider is not available.
+ */
+ public byte[] encryptData(byte[] data) throws NoSuchPaddingException, NoSuchAlgorithmException,
+ CertificateException, IOException, InvalidKeyException, UnrecoverableKeyException,
+ KeyStoreException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException,
+ NoSuchProviderException {
+
+ keyStore.load(null);
+ SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
+ String TRANSFORMATION = buildTransformation(secretKey);
+
+ Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ byte[] iv = cipher.getIV();
+ if (TRANSFORMATION.contains("/GCM/")) {
+ assert iv.length == IV_GCM_AES : "AES GCM IV length not matching.";
+ } else if(TRANSFORMATION.contains("DESede")) {
+ assert iv.length == IV_CBC_DES : "DES CBC IV length not matching.";
+ } else {
+ assert iv.length == IV_CBC_AND_CTR_AES : "AES CBC and CTR IV length not matching.";
+ }
+ byte[] encryptedData = cipher.doFinal(data);
+ ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + encryptedData.length);
+ byteBuffer.put(iv);
+ byteBuffer.put(encryptedData);
+ return byteBuffer.array();
+ }
+
+ /**
+ * Decrypts the given encrypted data using a symmetric key stored in the Android KeyStore.
+ *
+ * This method takes encrypted data as input and decrypts it using a symmetric key retrieved from the Android KeyStore.
+ * The decryption process supports GCM, CBC and CTR transformations. The initialization vector (IV)
+ * is extracted from the beginning of the encrypted data. In the case of 3DES only CBC is used, which needs a different IV size than with AES.
+ * The method initializes a {@link Cipher} instance with the appropriate
+ * transformation, loads the Android KeyStore, retrieves the symmetric key, and initializes the cipher in decryption mode with the
+ * retrieved key and the extracted IV. Finally, the encrypted data is decrypted using the cipher's {@code doFinal} method, and the
+ * original plaintext data is returned as a byte array.
+ *
+ * @param encryptedData The encrypted data to be decrypted, represented as a byte array.
+ * @return A byte array representing the decrypted data.
+ * @throws NoSuchPaddingException if the requested padding scheme is not available.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws CertificateException if there is an issue loading the certificate chain.
+ * @throws IOException if there is an I/O error during the operation.
+ * @throws InvalidAlgorithmParameterException if the IV parameter is invalid.
+ * @throws InvalidKeyException if the key cannot be cast to a SecretKey.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ * @throws IllegalBlockSizeException if the data length is invalid for the decryption algorithm.
+ * @throws BadPaddingException if the data could not be padded correctly for decryption.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the requested security provider is not available.
+ */
+ public byte[] decryptData(byte[] encryptedData) throws NoSuchPaddingException, NoSuchAlgorithmException,
+ CertificateException, IOException, InvalidAlgorithmParameterException, InvalidKeyException,
+ UnrecoverableKeyException, KeyStoreException, IllegalBlockSizeException, BadPaddingException,
+ InvalidKeySpecException, NoSuchProviderException {
+ keyStore.load(null);
+ SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
+ String TRANSFORMATION = buildTransformation(secretKey);
+ Cipher cipher = Cipher.getInstance(TRANSFORMATION);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedData);
+ byte[] iv;
+ if (TRANSFORMATION.contains("/GCM/")) {
+ assert byteBuffer.capacity() > IV_GCM_AES : "Data is not at least IV size in length.";
+ iv = new byte[IV_GCM_AES];
+ byteBuffer.get(iv);
+ encryptedData = new byte[byteBuffer.remaining()];
+ byteBuffer.get(encryptedData);
+ GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_SIZE_GCM, iv);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
+ } else {
+ if(TRANSFORMATION.contains("DESede")){
+ assert byteBuffer.capacity() > IV_CBC_DES : "Data is not at least IV size in length.";
+ iv = new byte[IV_CBC_DES];
+ }
+ else {
+ assert byteBuffer.capacity() > IV_CBC_AND_CTR_AES : "Data is not at least IV size in length.";
+ iv = new byte[IV_CBC_AND_CTR_AES];
+ }
+ byteBuffer.get(iv);
+ encryptedData = new byte[byteBuffer.remaining()];
+ byteBuffer.get(encryptedData);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
+ }
+ return cipher.doFinal(encryptedData);
+ }
+
+ /**
+ * Generates a new asymmetric key pair and saves it into the Android KeyStore.
+ *
+ * This method generates a new asymmetric key pair suitable for signing and verifying data.
+ * The key pair is stored in the Android KeyStore.
+ * The method configures the key pair generator with specific parameters, like the digest algorithms to be supported,
+ * the signature padding scheme, and whether the key is backed by the strong box feature for enhanced security.
+ * The generated key pair consists of a private key for signing and a corresponding public key for verification.
+ * The supported algorithms are RSA and EC. The keyGenInfo string is checked for its pattern to ensure that no incorrect values have been transferred.
+ *
+ * @param key_id The unique identifier under which the key pair will be stored in the KeyStore.
+ * @param keyGenInfo A string containing key generation parameters separated by semicolons. Expected formats: RSA: "KEY_ALGORITHM;KEY_SIZE;HASH;PADDING", EC: "KEY_ALGORITHM;CURVE;HASH".
+ * @throws CertificateException if there is an issue creating the certificate for the key pair.
+ * @throws IOException for I/O errors such as incorrect passwords.
+ * @throws NoSuchAlgorithmException if the generation algorithm does not exist or the keystore doesn't exist.
+ * @throws InvalidAlgorithmParameterException for invalid or nonexistent parameters.
+ * @throws NoSuchProviderException if the provider does not exist.
+ * @throws KeyStoreException if there is an error accessing the keystore or the key name is already used.
+ */
+ public void generateKeyPair(String key_id, String keyGenInfo) throws CertificateException, IOException,
+ NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException,
+ KeyStoreException {
+ Pattern pattern = Pattern.compile("^(RSA|EC);(\\d+|secp256r1|secp384r1|secp521r1);(SHA-256)(;PKCS1|;NoPadding)?$");
+ Matcher matcher = pattern.matcher(keyGenInfo);
+ assert matcher.matches() : "Generate KeyPair keyGenInfo is not valid.";
+ String[] keyGenInfoArr = keyGenInfo.split(";");
+ String KEY_ALGORITHM = keyGenInfoArr[0];
+ String HASH = keyGenInfoArr[2];
+
+ KEY_NAME = key_id;
+ keyStore.load(null);
+
+ // Check if a key with the given key_id already exists
+ if (keyStore.containsAlias(KEY_NAME)) {
+ throw new KeyStoreException("Key with name " + KEY_NAME + " already exists.");
+ }
+
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM, ANDROID_KEY_STORE);
+ if (KEY_ALGORITHM.contains("EC")) {
+ String CURVE = keyGenInfoArr[1];
+ keyPairGen.initialize(
+ new KeyGenParameterSpec.Builder(
+ KEY_NAME,
+ KeyProperties.PURPOSE_SIGN)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec(CURVE))
+ .setDigests(HASH)
+ .build());
+
+ } else {
+ int KEY_SIZE = Integer.parseInt(keyGenInfoArr[1]);
+ String PADDING = keyGenInfoArr[3];
+ keyPairGen.initialize(new KeyGenParameterSpec.Builder(KEY_NAME,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(KEY_SIZE)
+ .setDigests(HASH)
+ .setSignaturePaddings(PADDING)
+ .setIsStrongBoxBacked(true)
+ .build());
+ }
+ keyPairGen.generateKeyPair();
+ }
+
+ /**
+ * Signs the given data using a private key stored in the Android KeyStore.
+ *
+ * This method signs the input data with a private key obtained from the Android KeyStore.
+ * It initializes a {@link Signature} instance with the appropriate algorithm, which is determined dynamically based on the private key's specifications.
+ * The Android KeyStore is loaded, and the private key associated with a predefined alias is retrieved.
+ * The signature object is then initialized in sign mode with this private key.
+ * After updating the signature object with the plaintext data,
+ * the method completes the signing process by invoking the signature object's {@code sign} method.
+ * Finally, the method returns the resulting signature as a byte array, which can be used for verification purposes.
+ *
+ * @param data The data to be signed, provided as a byte array.
+ * @return A byte array representing the digital signature of the input data.
+ * @throws NoSuchAlgorithmException if the requested signature algorithm is not supported.
+ * @throws UnrecoverableKeyException if the private key cannot be retrieved from the keystore.
+ * @throws KeyStoreException if there is an issue accessing the keystore.
+ * @throws InvalidKeyException if the key cannot be cast to a {@link PrivateKey}.
+ * @throws SignatureException if there is an error during the signing process.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the requested security provider is not available.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ */
+ public byte[] signData(byte[] data) throws NoSuchAlgorithmException, UnrecoverableKeyException,
+ KeyStoreException, InvalidKeyException, SignatureException, InvalidKeySpecException, NoSuchProviderException, CertificateException, IOException {
+ keyStore.load(null);
+ Signature signature = Signature.getInstance(buildSignatureAlgorithm((PrivateKey) keyStore.getKey(KEY_NAME, null)));
+ signature.initSign((PrivateKey) keyStore.getKey(KEY_NAME, null));
+ signature.update(data);
+ return signature.sign();
+ }
+
+ /**
+ * Verifies the given data against a signature produced by a private key stored in the Android KeyStore.
+ *
+ * This method compares the provided data with the given signature, using the corresponding public key extracted from the Android KeyStore.
+ * It initializes a {@link Signature} instance with the appropriate algorithm, determined dynamically based on the public key's specifications.
+ * The method then loads the Android KeyStore, retrieves the public key associated with a predefined alias,
+ * and initializes the signature object in verify mode with this public key.
+ * After updating the signature object with the plaintext data, the method verifies the signature against the provided signed bytes
+ * and returns the result of this verification.
+ *
+ * @param data The data that was originally signed, provided as a byte array.
+ * @param signedBytes The signature produced by signing the original data, provided as a byte array.
+ * @return {@code true} if the signature matches the data; {@code false} otherwise.
+ * @throws SignatureException if there is an error during the verification process.
+ * @throws InvalidKeyException if the key cannot be cast to a {@link PrivateKey}.
+ * @throws KeyStoreException if there is an issue accessing the keystore.
+ * @throws NoSuchAlgorithmException if the requested signature algorithm is not supported.
+ * @throws UnrecoverableKeyException if the public key cannot be retrieved from the keystore.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the requested security provider is not available.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ */
+ public boolean verifySignature(byte[] data, byte[] signedBytes) throws SignatureException, InvalidKeyException,
+ KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, InvalidKeySpecException, NoSuchProviderException, CertificateException, IOException {
+ keyStore.load(null);
+ Signature verificationSignature = Signature.getInstance(buildSignatureAlgorithm((PrivateKey) keyStore.getKey(KEY_NAME, null)));
+ verificationSignature.initVerify(keyStore.getCertificate(KEY_NAME).getPublicKey());
+ verificationSignature.update(data);
+ return verificationSignature.verify(signedBytes);
+ }
+
+ /**
+ * Sets the `KEY_NAME` to the provided key identifier.
+ *
+ * This method assigns the `KEY_NAME` field with the given `key_id`.
+ * This is typically used before loading a key with the specified identifier.
+ * It ensures that the `KEY_NAME` is set correctly for subsequent cryptographic operations
+ * involving the specified key.
+ *
+ * @param key_id The unique identifier of a key to be set as `KEY_NAME`.
+ * @throws KeyStoreException if there is an issue accessing the keystore.
+ * @throws UnrecoverableKeyException if the key cannot be retrieved from the keystore.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ */
+ public void loadKey(String key_id) throws KeyStoreException, UnrecoverableKeyException, CertificateException, IOException, NoSuchAlgorithmException {
+ keyStore.load(null);
+ if (keyStore.containsAlias(key_id)) KEY_NAME = key_id;
+ else
+ throw new UnrecoverableKeyException("The key alias '" + key_id + "' does not exist in the KeyStore.");
+ }
+
+ /**
+ * Constructs the transformation string for a given key, which is used to initialize a {@link Cipher} instance.
+ *
+ * This method loads the Android KeyStore and retrieves key-specific metadata using {@link KeyInfo}.
+ * From those it builds a transformation string based on the key's algorithm, block modes, and padding schemes. The transformation
+ * string follows the format "algorithm/block-mode/padding". It supports both symmetric keys ({@link SecretKey}) and asymmetric keys
+ * ({@link PrivateKey}). For symmetric keys, it retrieves encryption paddings; for asymmetric keys, it retrieves signature paddings.
+ *
+ * @param key The key for which the transformation string is to be built. It can be either a {@link SecretKey} or a {@link PrivateKey}.
+ * @return A string representing the transformation in the format "algorithm/mode/padding".
+ * @throws NullPointerException if the key or any retrieved metadata is null.
+ * @throws CertificateException if there is an issue with the certificate chain.
+ * @throws IOException if there is an I/O error during the operation.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the requested security provider is not available.
+ * @throws KeyStoreException if there is an error accessing the keystore or if the key type is unsupported.
+ */
+ private String buildTransformation(Key key) throws NullPointerException, CertificateException,
+ IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, KeyStoreException {
+ keyStore.load(null);
+ KeyInfo keyInfo;
+ String keyAlgorithm = key.getAlgorithm();
+ String keyPadding;
+
+ if (key instanceof SecretKey) {
+ SecretKey secretKey = (SecretKey) key;
+ SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKey.getAlgorithm(), ANDROID_KEY_STORE);
+ keyInfo = (KeyInfo) factory.getKeySpec(secretKey, KeyInfo.class);
+ assert(keyInfo.getEncryptionPaddings().length > 0);
+ keyPadding = keyInfo.getEncryptionPaddings()[0];
+ } else if (key instanceof PrivateKey) {
+ PrivateKey privateKey = (PrivateKey) key;
+ KeyFactory factory = KeyFactory.getInstance(privateKey.getAlgorithm(), ANDROID_KEY_STORE);
+ keyInfo = factory.getKeySpec(privateKey, KeyInfo.class);
+ assert(keyInfo.getSignaturePaddings().length > 0);
+ keyPadding = keyInfo.getSignaturePaddings()[0];
+ } else {
+ throw new KeyStoreException("Unsupported key type");
+ }
+ assert(keyInfo.getBlockModes().length > 0);
+ return keyAlgorithm + "/" + keyInfo.getBlockModes()[0] + "/" + keyPadding;
+ }
+
+ /**
+ * Constructs the signature algorithm string based on the provided private key.
+ *
+ * This method retrieves metadata from the given {@link PrivateKey} to dynamically construct
+ * the signature algorithm string. It uses the {@link KeyFactory} to obtain the {@link KeyInfo}
+ * of the private key, which includes details such as the digest algorithms supported by the key.
+ * The method then combines the hash algorithm and the private key algorithm to form the signature
+ * algorithm string.
+ *
+ *
+ * @param privateKey The {@link PrivateKey} for which the signature algorithm string is to be constructed.
+ * @return A string representing the signature algorithm, which combines the hash algorithm and the key algorithm.
+ * @throws NoSuchAlgorithmException If the algorithm of the private key is not available.
+ * @throws NoSuchProviderException If the specified provider is not available.
+ * @throws InvalidKeySpecException If the key specification is invalid or cannot be retrieved.
+ */
+ private String buildSignatureAlgorithm(PrivateKey privateKey) throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidKeySpecException {
+ KeyFactory keyFactory = KeyFactory.getInstance(privateKey.getAlgorithm(), ANDROID_KEY_STORE);
+ KeyInfo keyInfo = keyFactory.getKeySpec(privateKey, KeyInfo.class);
+ assert(keyInfo.getDigests().length > 0);
+ String hashAlgorithm = keyInfo.getDigests()[0].replaceAll("-", "");
+ String algorithm = privateKey.getAlgorithm();
+ if (algorithm.contains("EC")) {
+ algorithm += "DSA";
+ }
+ return hashAlgorithm + "with" + algorithm;
+ }
+
+}
\ No newline at end of file
diff --git a/src/tpm/android/knox/java/RustDef.java b/src/tpm/android/knox/java/RustDef.java
new file mode 100644
index 00000000..a952776b
--- /dev/null
+++ b/src/tpm/android/knox/java/RustDef.java
@@ -0,0 +1,275 @@
+package tpm.android.knox.java;
+
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.ArrayList;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * This class provides the method declarations that are the interface for the JNI.
+ * The first part are Rust-methods that can be called from other Java-classes,
+ * while the second part contains full Java-methods that can be called from Rust.
+ *
+ * This class also loads the compiled Rust coda as a dynamic library
+ *
+ * All methods defined in this class hava to have a corresponding method defined in lib.rs,
+ * with the same method name and corresponding input and output parameters, according to this table:
+ *
+ * Rust Java
+ * ------------------------
+ * i32 int
+ * bool boolean
+ * char char
+ * i8 byte
+ * f32 float
+ * f64 double
+ * i64 long
+ * i16 short
+ * String String
+ * Vec ArrayList
+ * Box<[u8]> byte[]
+ * jni::JObject<'env> (any Java object as input type)
+ * jni::jobject (any Java object as output)
+ *
+ * @noinspection unused - Methods called from Rust are not recognized as being in use
+ */
+class RustDef {
+
+ /*
+ CryptoManger object for execution of methods
+ */
+ static CryptoManager cryptoManager;
+
+ static {
+ // This call loads the dynamic library containing the Rust code.
+ System.loadLibrary("vulcanslimes");
+ }
+
+ //----------------------------------------------------------------------------------------------
+ //Rust methods that can be called from Java
+
+ /**
+ * Proof of concept - shows type conversion
+ * DO NOT USE
+ */
+ static native ArrayList special(ArrayList input1, int input2);
+
+
+ static native String callRust();
+
+ static native byte[] demoEncrypt(byte[] data);
+
+ static native void demoCreate(String key_id, String key_gen_info);
+
+ static native void demoInit();
+
+ static native byte[] demoDecrypt(byte[] data);
+
+ static native byte[] demoSign(byte[] data);
+
+ static native boolean demoVerify(byte[] data, byte[] signed_data);
+
+ static native void demoLoad(String key_id);
+
+ //----------------------------------------------------------------------------------------------
+ //Java methods that can be called from Rust
+
+ /*
+ Proof of concept method - get called from Rust when callRust() gets called
+ DO NOT USE
+ */
+ static void callback() {
+ System.out.println("Callback successful");
+ }
+
+ /**
+ * Creates a new cryptographic key identified by {@code key_id}.
+ *
+ * This method generates a new cryptographic key within the TPM. The key is made persistent
+ * and associated with the provided {@code key_id}, which uniquely identifies the key
+ * so that it can be retrieved later for cryptographic operations, and {@code keyGenInfo},
+ * which holds the information about the key to be generated.
+ *
+ * @param key_id a String that uniquely identifies the key to be created within the TPM.
+ * @param keyGenInfo additional information required for key generation, specifying parameters such as
+ * key size, algorithm, or other configuration details.
+ * @throws InvalidAlgorithmParameterException if the specified key generation parameters are invalid or
+ * incompatible with the key generation process.
+ * @throws CertificateException if there is an issue with certificate handling, such as failures
+ * in certificate creation or validation.
+ * @throws IOException if an I/O error occurs during key generation or processing.
+ * @throws NoSuchAlgorithmException if the requested cryptographic algorithm for key generation is
+ * not available or supported.
+ * @throws KeyStoreException if there is an error accessing the keystore, such as a failure
+ * to store the newly generated key.
+ * @throws NoSuchProviderException if the requested security provider is not available or supported.
+ */
+ static void create_key(String key_id, String keyGenInfo) throws InvalidAlgorithmParameterException, CertificateException,
+ IOException, NoSuchAlgorithmException, KeyStoreException, NoSuchProviderException {
+ if (keyGenInfo.contains("RSA") || keyGenInfo.contains("EC"))
+ cryptoManager.generateKeyPair(key_id, keyGenInfo);
+ else cryptoManager.genKey(key_id, keyGenInfo);
+ }
+
+ /**
+ * Loads an existing cryptographic key identified by {@code key_id}.
+ *
+ * This method loads an existing cryptographic key from the TPM. The loaded key is
+ * associated with the provided {@code key_id}, which uniquely identifies the key
+ * so that it can be retrieved later for cryptographic operations.
+ *
+ * @param key_id a String that uniquely identifies the key to be loaded from the TPM.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore, typically due to
+ * incorrect or inaccessible key information.
+ * @throws KeyStoreException if there is an error accessing the keystore, such as a failure to load
+ * or initialize the keystore.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ */
+ static void load_key(String key_id) throws UnrecoverableKeyException, KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {
+ cryptoManager.loadKey(key_id);
+ }
+
+ /**
+ * Initializes the TPM (Trusted Platform Module) module and returns a handle for further operations.
+ *
+ * This method initializes the TPM context and prepares it for use. It should be called before performing
+ * any other operations with the TPM. Upon initialization, it sets up the necessary configurations and
+ * resources required for cryptographic operations involving the TPM.
+ *
+ * @throws KeyStoreException if the KeyStore Provider does not exist or fails to initialize, indicating issues
+ * with the key store setup process.
+ */
+ static void initialize_module() throws KeyStoreException {
+ cryptoManager = new CryptoManager();
+
+ }
+
+ /**
+ * Signs the given data using the key managed by the TPM.
+ *
+ * This method signs the provided data using the key managed by the TPM (Trusted Platform Module). The data to be
+ * signed is represented as a byte array. The signing process produces a signature for the data, which is returned as
+ * a byte array containing the signed data.
+ *
+ * @param data a byte array representing the data to be signed.
+ * @return the signed data as a byte array.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ * @throws SignatureException if the signature process encounters an error.
+ * @throws InvalidKeyException if the key used for signing is invalid.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the provider is not available.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ */
+ static byte[] sign_data(byte[] data) throws UnrecoverableKeyException, NoSuchAlgorithmException,
+ KeyStoreException, SignatureException, InvalidKeyException, InvalidKeySpecException, NoSuchProviderException, CertificateException, IOException {
+ return cryptoManager.signData(data);
+ }
+
+ /**
+ * Verifies the signature of the given data using the key managed by the TPM.
+ *
+ * This method verifies the signature of the provided data against a known signature using the key managed by the TPM
+ * (Trusted Platform Module). Both the data and the signature are represented as byte arrays. The verification process
+ * validates whether the signature matches the data, returning true if the signature is valid and false otherwise.
+ *
+ * @param data a byte array representing the data to be verified.
+ * @param signature a byte array representing the signature to be verified against the data.
+ * @return true if the signature is valid, false otherwise.
+ * @throws SignatureException if the signature verification process encounters an error.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws InvalidKeyException if the key used for verification is invalid.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws NoSuchProviderException if the provider is not available.
+ * @throws CertificateException if there is an error processing certificates.
+ * @throws IOException if there is an I/O error while interacting with the keystore.
+ */
+ static boolean verify_signature(byte[] data, byte[] signature) throws SignatureException, KeyStoreException,
+ NoSuchAlgorithmException, InvalidKeyException, UnrecoverableKeyException, InvalidKeySpecException,
+ NoSuchProviderException, CertificateException, IOException {
+ return cryptoManager.verifySignature(data, signature);
+ }
+
+ /**
+ * Encrypts the given data using the key managed by the TPM.
+ *
+ * This method encrypts the provided data using the key managed by the TPM (Trusted Platform Module).
+ * The data to be encrypted is represented as a byte array. The encryption process is performed using cryptographic
+ * operations managed by the {@link CryptoManager}. The encrypted data is returned as a byte array.
+ *
+ * This method is called from Rust code, indicating that it may be invoked as part of an integration with a Rust
+ * application or library.
+ *
+ * @param data a byte array representing the data to be encrypted.
+ * @return a byte array containing the encrypted data.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore.
+ * @throws NoSuchPaddingException if the padding scheme is not available.
+ * @throws IllegalBlockSizeException if the block size is invalid for the encryption algorithm.
+ * @throws CertificateException if there is an issue loading the certificate chain.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws IOException if there is an I/O error during the operation.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ * @throws BadPaddingException if the data padding is incorrect for encryption.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws InvalidKeyException if the key is invalid for encryption.
+ * @throws NoSuchProviderException if the provider is not available.
+ */
+ static byte[] encrypt_data(byte[] data) throws UnrecoverableKeyException,
+ NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException,
+ IOException, KeyStoreException, BadPaddingException, InvalidKeySpecException, InvalidKeyException,
+ NoSuchProviderException {
+ return cryptoManager.encryptData(data);
+ }
+
+ /**
+ * Decrypts the given data using the key managed by the TPM.
+ *
+ * This method decrypts the provided encrypted data using the key managed by the TPM (Trusted Platform Module).
+ * The encrypted data is represented as a byte array. The decryption process is performed using cryptographic
+ * operations managed by the {@link CryptoManager}. The decrypted data is returned as a byte array.
+ *
+ * This method is called from Rust code, indicating that it may be invoked as part of an integration with a Rust
+ * application or library.
+ *
+ * @param encrypted_data a byte array representing the data to be decrypted.
+ * @return a byte array containing the decrypted data.
+ * @throws InvalidAlgorithmParameterException if the algorithm parameters are invalid for decryption.
+ * @throws UnrecoverableKeyException if the key cannot be recovered from the keystore.
+ * @throws NoSuchPaddingException if the padding scheme is not available.
+ * @throws IllegalBlockSizeException if the block size is invalid for the decryption algorithm.
+ * @throws CertificateException if there is an issue loading the certificate chain.
+ * @throws NoSuchAlgorithmException if the requested algorithm is not available.
+ * @throws IOException if there is an I/O error during the operation.
+ * @throws KeyStoreException if there is an error accessing the keystore.
+ * @throws BadPaddingException if the data padding is incorrect for decryption.
+ * @throws InvalidKeySpecException if the key specification is invalid.
+ * @throws InvalidKeyException if the key is invalid for decryption.
+ * @throws NoSuchProviderException if the provider is not available.
+ */
+ static byte[] decrypt_data(byte[] encrypted_data) throws InvalidAlgorithmParameterException, UnrecoverableKeyException,
+ NoSuchPaddingException, IllegalBlockSizeException, CertificateException, NoSuchAlgorithmException,
+ IOException, KeyStoreException, BadPaddingException, InvalidKeySpecException, InvalidKeyException,
+ NoSuchProviderException {
+ return cryptoManager.decryptData(encrypted_data);
+ }
+}
\ No newline at end of file
diff --git a/src/tpm/android/knox/key_handle.rs b/src/tpm/android/knox/key_handle.rs
new file mode 100644
index 00000000..05c8f303
--- /dev/null
+++ b/src/tpm/android/knox/key_handle.rs
@@ -0,0 +1,82 @@
+use tracing::instrument;
+
+use crate::{
+ common::{error::SecurityModuleError, traits::key_handle::KeyHandle},
+ tpm::android::knox::interface::jni::RustDef
+};
+
+use super::KnoxProvider;
+
+/// Implements the `Provider` trait, providing cryptographic operations
+/// such as signing, encryption, decryption, and signature verification for the TPM Knox Vault.
+///
+/// This implementation is specific to Samsung Knox Vault and uses the Android Keystore API for all cryptographic operations
+/// In theory, this should also work for other TPMs on Android phones, but it is only tested with Samsung Knox Vault
+impl KeyHandle for KnoxProvider {
+ /// Signs data using the previously loaded cryptographic key.
+ ///
+ /// This method hashes the input data using SHA-256 and then signs the hash.
+ /// The algorithm used for signing is determined by the currently loaded key.
+ /// If no key is loaded, an Error is returned.
+ ///
+ /// # Arguments
+ ///
+ /// * `data` - The data to be signed.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing the signature as a `Vec` on success, or a `SecurityModuleError` on failure.
+ #[instrument]
+ fn sign_data(&self, data: &[u8]) -> Result, SecurityModuleError> {
+ RustDef::sign_data(&self.get_env()?, data)
+ }
+
+ /// Decrypts the data with the currently loaded key.
+ /// The algorithm used for decryption is determined by the currently loaded key.
+ /// If no key is loaded, an Error is returned.
+ /// # Arguments
+ ///
+ /// * `encrypted_data` - The data to be decrypted.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing the decrypted data as a `Vec` on success, or a `SecurityModuleError` on failure.
+ #[instrument]
+ fn decrypt_data(&self, encrypted_data: &[u8]) -> Result, SecurityModuleError> {
+ RustDef::decrypt_data(&self.get_env()?, encrypted_data)
+ }
+
+ /// Encrypts the data with the currently loaded key.
+ /// The algorithm used for Encryption is determined by the currently loaded key.
+ /// If no key is loaded, an Error is returned.
+ /// # Arguments
+ ///
+ /// * `data` - The data to be encrypted.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing the encrypted data as a `Vec` on success, or a `SecurityModuleError` on failure.
+ #[instrument]
+ fn encrypt_data(&self, data: &[u8]) -> Result, SecurityModuleError> {
+ RustDef::encrypt_data(&self.get_env()?, data)
+ }
+
+ /// Verifies a signature against the provided data.
+ ///
+ /// This method hashes the input data using SHA-256 and then verifies the signature with the currently loaded key.
+ /// The algorithm used for verification is determined by the currently loaded key.
+ /// If no key is loaded, an Error is returned.
+ /// # Arguments
+ ///
+ /// * `data` - The original data associated with the signature.
+ /// * `signature` - The signature to be verified.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` indicating whether the signature is valid (`true`) or not (`false`),
+ /// or a `SecurityModuleError` on failure.
+ #[instrument]
+ fn verify_signature(&self, data: &[u8], signature: &[u8]) -> Result {
+ RustDef::verify_signature(&self.get_env()?, data, signature)
+ }
+}
diff --git a/src/tpm/android/knox/mod.rs b/src/tpm/android/knox/mod.rs
index 8b137891..08c14604 100644
--- a/src/tpm/android/knox/mod.rs
+++ b/src/tpm/android/knox/mod.rs
@@ -1 +1,129 @@
+use std::any::Any;
+use std::fmt;
+use std::fmt::{Debug, Formatter};
+use robusta_jni::jni::{JavaVM, JNIEnv};
+use tracing::instrument;
+
+use crate::common::crypto::algorithms::encryption::{AsymmetricEncryption, BlockCiphers};
+use crate::common::traits::module_provider_config::ProviderConfig;
+use crate::SecurityModuleError;
+
+mod interface;
+pub mod key_handle;
+pub mod provider;
+
+/// A TPM-based cryptographic provider for managing cryptographic keys and performing
+/// cryptographic operations in a Samsung environment. This provider uses the Java Native Interface
+/// and the Android Keystore API to access the TPM "Knox Vault" developed by Samsung. In theory,
+/// this code should also work for other TPMs on Android Devices, though it is only tested with Knox Vault
+#[repr(C)]
+pub struct KnoxProvider {
+ config: Option,
+}
+
+///implements the Debug trait for KnoxProvider to facilitate logging
+impl Debug for KnoxProvider {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("KnoxProvider")
+ .field("config", &self.config)
+ .finish()
+ }
+}
+
+///Provides functions to manage the KnoxProvider and the stored values within
+impl KnoxProvider {
+ /// Constructs a new `TpmProvider`.
+ ///
+ /// # Returns
+ ///
+ /// A new empty instance of `TpmProvider`.
+ #[instrument]
+ pub fn new() -> Self {
+ Self { config: None }
+ }
+
+ /// Sets the configuration for the `Knox` instance.
+ ///
+ /// # Arguments
+ ///
+ /// * `config` - A `KnoxConfig` instance that contains the configuration settings.
+ fn set_config(&mut self, config: KnoxConfig) -> () {
+ self.config = Some(config);
+ }
+
+ ///Get the JavaVM stored in &self and provides the JNIEnv based on it
+ /// # Returns
+ ///
+ /// a JNIEnv on success to be used for JNI method calls.
+ /// If the KnoxConfig has not been loaded yet or contains an invalid JavaVM, an error is returned
+ fn get_env(&self) -> Result {
+ if self.config.is_none() { return Err(SecurityModuleError::InitializationError(String::from("No key loaded"))); }
+ let conf = self.config
+ .as_ref()
+ .ok_or(
+ SecurityModuleError::InitializationError(String::from("failed to store config data")))?;
+ conf.vm
+ .get_env()
+ .map_err(|_| SecurityModuleError::InitializationError(String::from("failed to retrieve JNIEnv")))
+ }
+
+ ///Converts the config parameter to a KnoxConfig
+ fn downcast_config(config: Box) -> Result {
+ Ok(*config
+ .downcast::()
+ .map_err(|err| SecurityModuleError::InitializationError(format!("wrong config provided: {:?}", err)))?)
+ }
+}
+
+/// A struct defining the needed values for the create_key() and load_key() functions
+/// At any time, either a key_algorithm OR a sym_algorithm must be supplied, not both.
+/// For hashing operations, SHA-256 is always used since it is the only one available on Knox Vault
+/// The last needed parameter is a JavaVM that is needed to call the Android KeystoreAPI
+pub struct KnoxConfig {
+ pub key_algorithm: Option,
+ pub sym_algorithm: Option,
+ pub vm: JavaVM,
+}
+
+/// implements the debug trait for KnoxConfig for logging
+impl Debug for KnoxConfig {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("KnoxConfig")
+ .field("key_algorithm", &self.key_algorithm)
+ .field("sym_algorithm", &self.sym_algorithm)
+ .field("JavaVM", &"Contains a JavaVM to interact with Java")
+ .finish()
+ }
+}
+
+///implements ProviderConfig for KnoxConfig
+impl ProviderConfig for KnoxConfig {
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+}
+
+/// Implements KnoxConfig and provides a constructor
+impl KnoxConfig {
+ /// creates a new KnoxConfig
+ /// At any time, either a key_algorithm OR a sym_algorithm must be supplied, not both.
+ /// Otherwise, load_key() or create_key() will return an Error.
+ /// The last needed parameter is a JavaVM that is needed to call the Android KeystoreAPI
+ pub fn new(
+ key_algorithm: Option,
+ sym_algorithm: Option,
+ vm: JavaVM,
+ ) -> Result {
+ if (key_algorithm.is_none() && sym_algorithm.is_none()) ||
+ (key_algorithm.is_some() && sym_algorithm.is_some()) {
+ return Err(SecurityModuleError::InitializationError(
+ String::from("Either sym_algorithm OR key_algorithm must be Some(_)")));
+ }
+ Ok(Self {
+ key_algorithm,
+ sym_algorithm,
+ vm,
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/tpm/android/knox/provider.rs b/src/tpm/android/knox/provider.rs
new file mode 100644
index 00000000..0d80b6ac
--- /dev/null
+++ b/src/tpm/android/knox/provider.rs
@@ -0,0 +1,171 @@
+use std::any::Any;
+
+use tracing::instrument;
+
+use crate::{
+ common::{
+ crypto::algorithms::{
+ encryption::{
+ AsymmetricEncryption,
+ BlockCiphers,
+ EccCurves,
+ EccSchemeAlgorithm,
+ SymmetricMode,
+ },
+ KeyBits
+ },
+ error::SecurityModuleError,
+ traits::module_provider::Provider,
+ },
+ tpm::{
+ android::knox::{
+ interface::jni::RustDef,
+ KnoxProvider
+ },
+ core::error::TpmError::UnsupportedOperation
+ }
+};
+
+/// Implements the `Provider` trait, providing cryptographic operations utilizing a TPM.
+///
+/// This implementation is specific to Samsung Knox Vault and uses the Android Keystore API for all cryptographic operations
+/// In theory, this should also work for other TPMs on Android phones, but it is only tested with Samsung Knox Vault
+impl Provider for KnoxProvider {
+ /// Creates a new cryptographic key identified by `key_id`.
+ ///
+ /// This method creates a persistent cryptographic key using the specified algorithm
+ /// and identifier, making it retrievable for future operations. The key is created
+ /// and stored in Knox Vault. This method also loads the key for further usage, therefore it is
+ /// not necessary to load a key after creating it.
+ ///
+ /// # Arguments
+ ///
+ /// * `key_id` - A string slice that uniquely identifies the key to be created.
+ /// * `Box) -> Result<(), SecurityModuleError> {
+ let config = Self::downcast_config(config)?;
+ let sym_alg = config.sym_algorithm;
+ let asym_alg = config.key_algorithm;
+
+ //Stores the vm for future access
+ self.set_config(config);
+
+ let key_algo;
+ if asym_alg.is_some() && sym_alg.is_none() {
+ key_algo = match asym_alg.expect("Already checked") {
+ AsymmetricEncryption::Rsa(bitslength) => {
+ match bitslength {
+ KeyBits::Bits2048 => { String::from("RSA;2048;SHA-256;PKCS1") }
+ _ => {
+ return Err(SecurityModuleError::Tpm(UnsupportedOperation(
+ format!("Unsupported asymmetric encryption algorithm: {:?}",
+ asym_alg))));
+ }
+ }
+ }
+ AsymmetricEncryption::Ecc(scheme) => {
+ match scheme {
+ EccSchemeAlgorithm::EcDsa(curve) => {
+ match curve {
+ EccCurves::P256 => { String::from("EC;secp256r1;SHA-256") }
+ EccCurves::P384 => { String::from("EC;secp384r1;SHA-256") }
+ EccCurves::P521 => { String::from("EC;secp521r1;SHA-256") }
+ // EccCurves::Curve25519 => { String::from("EC;X25519;SHA-256") } <- x25519 may ONLY be used for key agreement, not signing
+ _ => {
+ return Err(SecurityModuleError::Tpm(UnsupportedOperation(
+ format!("Unsupported asymmetric encryption algorithm: {:?}",
+ asym_alg))));
+ }
+ }
+ }
+ _ => {
+ return Err(SecurityModuleError::Tpm(UnsupportedOperation(
+ format!("Unsupported asymmetric encryption algorithm: {:?}",
+ asym_alg))));
+ }
+ }
+ }
+ };
+ } else if asym_alg.is_none() && sym_alg.is_some() {
+ key_algo = match sym_alg.expect("Already checked") {
+ BlockCiphers::Des => { String::from("DESede;168;CBC;PKCS7Padding") }
+
+ BlockCiphers::Aes(block, bitslength) => {
+ let mut rv = String::from("AES;");
+ match bitslength {
+ KeyBits::Bits128 => { rv += "128;"; }
+ KeyBits::Bits192 => { rv += "192;"; }
+ KeyBits::Bits256 => { rv += "256;"; }
+ _ => {
+ return Err(SecurityModuleError::Tpm(UnsupportedOperation(
+ format!("Unsupported symmetric encryption algorithm: {:?}", sym_alg))));
+ }
+ }
+ match block {
+ SymmetricMode::Gcm => { rv += "GCM;NoPadding" }
+ SymmetricMode::Cbc => { rv += "CBC;PKCS7Padding" }
+ SymmetricMode::Ctr => { rv += "CTR;NoPadding" }
+ _ => {
+ return Err(SecurityModuleError::Tpm(UnsupportedOperation(
+ format!("Unsupported symmetric encryption algorithm: {:?}", sym_alg))));
+ }
+ }
+ rv
+ }
+ _ => {
+ return Err(SecurityModuleError::Tpm(UnsupportedOperation(
+ format!("Unsupported symmetric encryption algorithm: {:?}", sym_alg))));
+ }
+ };
+ } else {
+ return Err(SecurityModuleError::CreationError(format!(
+ "wrong parameters in KnoxConfig:
+ Exactly one of either sym_algorithm or key_algorithm must be Some().\
+ sym_algorithm: {:?}\
+ key_algorithm: {:?}",
+ sym_alg,
+ asym_alg)));
+ }
+ RustDef::create_key(&self.get_env()?, String::from(key_id), key_algo)
+ }
+
+ /// Loads an existing cryptographic key identified by `key_id`.
+ ///
+ /// This method attempts to load a persisted cryptographic key by its identifier from the TPM.
+ /// If successful, it enables the key to be used for cryptographic operations.
+ ///
+ /// # Arguments
+ ///
+ /// * `key_id` - A string slice that uniquely identifies the key to be loaded.
+ /// * `Box) -> Result<(), SecurityModuleError> {
+ //Stores the vm for future access
+ let config = Self::downcast_config(config)?;
+ self.set_config(config);
+
+ RustDef::load_key(&self.get_env()?, key_id.to_string())
+ }
+
+ ///This function ordinarily initialises the HSM.
+ /// For our implementation, this is not needed. You do not need to call this method,
+ /// and it will always return Ok(()) if you do.
+ fn initialize_module(&mut self) -> Result<(), SecurityModuleError> {
+ Ok(())
+ }
+}
diff --git a/src/tpm/core/instance.rs b/src/tpm/core/instance.rs
index 6f49b4e3..fb88046e 100644
--- a/src/tpm/core/instance.rs
+++ b/src/tpm/core/instance.rs
@@ -129,7 +129,9 @@ impl TpmInstance {
AndroidTpmType::Keystore => Arc::new(Mutex::new(
crate::tpm::android::AndroidProvider::new(key_id),
)),
- AndroidTpmType::Knox => todo!(),
+ AndroidTpmType::Knox => Arc::new(Mutex::new(
+ crate::tpm::android::knox::KnoxProvider::new(),
+ )),
},
TpmType::None => todo!(),
}