1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <cpu-features.h> 18 #include <cutils/log.h> 19 #include <cutils/properties.h> 20 #include <jni.h> 21 #include <nativehelper/JNIHelp.h> 22 #include <openssl/aes.h> 23 #include <openssl/cpu.h> 24 #include <openssl/evp.h> 25 #include <stdint.h> 26 #include <string.h> 27 #include <sys/vfs.h> 28 #include <time.h> 29 #include <new> 30 31 #define TEST_EVP_CIPHER EVP_aes_256_cbc() 32 #define TEST_BUFSIZE (1 * 1024 * 1024) /* 1 MiB */ 33 #define TEST_ITERATIONS 100 /* MiB */ 34 #define TEST_THRESHOLD 2000 /* ms */ 35 36 /* 37 * Detect if filesystem is already encrypted looking at the file 38 * system type. It should be possible to check this first but fall 39 * back to checking a property value if this is not possible to 40 * verify. 41 */ 42 static jboolean checkEncryptedFileSystem() { 43 struct statfs buf; 44 if ((-1 != statfs("/data", &buf)) && 45 (buf.f_type == 0xf15f /* ecryptfs */)) { 46 return true; 47 } 48 return false; 49 } 50 51 /* 52 * Function: deviceIsEncrypted 53 * Purpose: Check the device is encrypted 54 * Parameters: none 55 * Returns: boolean: (true) if encrypted, (false) otherwise 56 * Exceptions: none 57 */ 58 static jboolean android_security_cts_EncryptionTest_deviceIsEncrypted(JNIEnv *, jobject) 59 { 60 if (checkEncryptedFileSystem()) { 61 return true; 62 } 63 64 char prop_value[PROP_VALUE_MAX]; 65 property_get("ro.crypto.state", prop_value, ""); 66 67 jboolean rc = !strcmp(prop_value, "encrypted"); 68 ALOGE("EncryptionTest::deviceIsEncrypted: %d", rc); 69 70 return rc; 71 } 72 73 static inline uint64_t ns() 74 { 75 struct timespec ts; 76 clock_gettime(CLOCK_MONOTONIC, &ts); 77 return (uint64_t)ts.tv_sec * 1000000000 + ts.tv_nsec; 78 } 79 80 /* 81 * Function: aesIsFast 82 * Purpose: Test if AES performance is sufficient to require encryption 83 * Parameters: none 84 * Returns: boolean: (true) if AES performance is acceptable, (false) otherwise 85 * Exceptions: InvalidKeyException if EVP_DecryptInit fails, OutOfMemoryError 86 * if memory allocation fails. 87 */ 88 static jboolean android_security_cts_EncryptionTest_aesIsFast(JNIEnv *env, jobject) 89 { 90 EVP_CIPHER_CTX ctx; 91 uint8_t *buf; 92 uint8_t key[EVP_CIPHER_key_length(TEST_EVP_CIPHER)]; 93 uint8_t iv[EVP_CIPHER_iv_length(TEST_EVP_CIPHER)]; 94 95 memset(key, 0x42, sizeof(key)); 96 memset(iv, 0x11, sizeof(iv)); 97 98 EVP_CIPHER_CTX_init(&ctx); 99 100 if (!EVP_DecryptInit(&ctx, TEST_EVP_CIPHER, key, iv)) { 101 jniThrowException(env, "java/security/InvalidKeyException", 102 "EVP_DecryptInit failed"); 103 return false; 104 } 105 106 buf = new (std::nothrow) uint8_t[TEST_BUFSIZE + 107 EVP_CIPHER_block_size(TEST_EVP_CIPHER)]; 108 109 if (!buf) { 110 jniThrowException(env, "java/lang/OutOfMemoryError", 111 "Failed to allocate test buffer"); 112 return false; 113 } 114 115 memset(buf, 0xF0, TEST_BUFSIZE); 116 117 int len; 118 uint64_t t = ns(); 119 120 for (int i = 0; i < TEST_ITERATIONS; ++i) { 121 EVP_DecryptUpdate(&ctx, buf, &len, buf, TEST_BUFSIZE); 122 } 123 124 t = ns() - t; 125 126 delete[] buf; 127 128 unsigned long ms = (unsigned long)(t / 1000000); 129 double speed = 130 (double)(TEST_ITERATIONS * TEST_BUFSIZE / (1024 * 1024)) * 1000.0 / ms; 131 132 ALOGE("EncryptionTest::aesIsFast: %u iterations in %lu ms (%.01lf MiB/s) " 133 "(threshold %u ms)", TEST_ITERATIONS, ms, speed, TEST_THRESHOLD); 134 135 return ms < TEST_THRESHOLD; 136 } 137 138 static JNINativeMethod gMethods[] = { 139 { "deviceIsEncrypted", "()Z", 140 (void *) android_security_cts_EncryptionTest_deviceIsEncrypted }, 141 { "aesIsFast", "()Z", 142 (void *) android_security_cts_EncryptionTest_aesIsFast } 143 }; 144 145 int register_android_security_cts_EncryptionTest(JNIEnv* env) 146 { 147 jclass clazz = env->FindClass("android/security/cts/EncryptionTest"); 148 return env->RegisterNatives(clazz, gMethods, 149 sizeof(gMethods) / sizeof(JNINativeMethod)); 150 } 151