HSM works

This commit is contained in:
Andras Slemmer 2017-06-14 18:04:25 +01:00
parent 294dc41fe6
commit 904252c0bb
19 changed files with 4951 additions and 145 deletions

View File

@ -35,4 +35,4 @@ include 'samples:bank-of-corda-demo'
include 'cordform-common' include 'cordform-common'
include 'doorman' include 'doorman'
include 'verify-enclave' include 'verify-enclave'
include 'sgx-jvm/sgx-signtool' include 'sgx-jvm/hsm-tool'

View File

@ -4,20 +4,16 @@ The build
Prerequisites Prerequisites
------------- -------------
* Install mercurial, gcc/g++, autoconf, automake, libtool, ocaml, protobuf, libprotobuf, libcurl, libopenssl, cmake, zlib headers, libunwind headers, libcurl headers, libprotobuf headers * Install mercurial, gcc/g++(6), autoconf, automake, ocaml, opendjk(8), libtool, python(2.7)
* Install proguard and symlink the proguard.jar by `ln -s <LOCATION OF proguard.jar> sgx-jvm/jvm-enclave/proguard.jar` * Make sure JAVA_HOME points to your jdk 8 installation
* Make sure CXX points to g++ (the project does NOT compile with other compilers like clang!)
* If your hardware supports SGX and you want to use it directly you need to install and load the sgx kernel module (verify by running `lsmod | grep isgx`) and have the sgx service running (on a systemd setup verify by running `systemctl status aesmd`). Note that this is only required for actually running the binary, the build should work fine without. * If your hardware supports SGX and you want to use it directly you need to install and load the sgx kernel module (verify by running `lsmod | grep isgx`) and have the sgx service running (on a systemd setup verify by running `systemctl status aesmd`). Note that this is only required for actually running the binary, the build should work fine without.
* The SGX SDK has a simulation mode that doesn't require hardware support. To use this edit `sgx-jvm/jvm-enclave/CMakeLists.txt` and change `set(SGX_USE_HARDWARE TRUE)` to `FALSE` * The SGX SDK has a simulation mode that doesn't require hardware support. To use this edit `sgx-jvm/jvm-enclave/CMakeLists.txt` and change `set(SGX_USE_HARDWARE TRUE)` to `FALSE`
Troubles with libprotobuf
-------------------------
Unless your distro is very old you will possibly encounter protobuf linker errors when building the SGX SDK. The current version assumes libprotobuf.so.8. If your distro supports downgrades try that, the library version of 2.5.0-4 should work. We may have to include the .so in the repo, or potentially create a pre-setup docker image or something to avoid all this fuss.
Toplevel Makefile targets Toplevel Makefile targets
------------------------- -------------------------
* `make` will download all other dependencies and build the sgx\_experiments binary, residing at `sgx-jvm/jvm-enclave/build/sgx\_experiments`. * `make` will download all other dependencies and build the sgx\_standalone\_verify binary, residing at `sgx-jvm/jvm-enclave/standalone/build/sgx\_standalone\_verify`, as well as a JNI .so residing at `sgx-jvm/jvm-enclave/jni/build/untrusted_corda_sgx.so`
* `make clean` will clean all build targets. * `make clean` will clean all build targets.
* `make distclean` will clean all build targets and downloaded dependencies. Ordinarily you shouldn't need to run this. * `make distclean` will clean all build targets and downloaded dependencies. Ordinarily you shouldn't need to run this.

View File

@ -42,7 +42,7 @@ jar {
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF' exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
manifest { manifest {
attributes('Main-Class' : 'com.r3cev.sgx.signtool.MainKt') attributes('Main-Class' : 'com.r3cev.sgx.hsmtool.MainKt')
} }
} }
@ -52,6 +52,3 @@ task copyConfig(type: Copy) {
} }
build.dependsOn copyConfig build.dependsOn copyConfig

View File

@ -9,10 +9,16 @@ import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import kotlin.system.exitProcess import kotlin.system.exitProcess
enum class Mode {
GenerateKey,
Sign
}
data class ToolConfig(val config: Config) { data class ToolConfig(val config: Config) {
constructor(args: Array<String>) : this({ constructor(args: Array<String>) : this({
val parser = OptionParser() val parser = OptionParser()
val sourcePathArg = parser.accepts("source").withRequiredArg().required() val modeArg = parser.accepts("mode").withRequiredArg().defaultsTo(Mode.Sign.name)
val sourcePathArg = parser.accepts("source").withRequiredArg()
val configPathArg = parser.accepts("config").withRequiredArg() val configPathArg = parser.accepts("config").withRequiredArg()
val profileArg = parser.accepts("profile").withRequiredArg().defaultsTo("dev") val profileArg = parser.accepts("profile").withRequiredArg().defaultsTo("dev")
val publicKeyOutputPathArg = parser.accepts("pubkey").withRequiredArg().defaultsTo("./pubkey.pem") val publicKeyOutputPathArg = parser.accepts("pubkey").withRequiredArg().defaultsTo("./pubkey.pem")
@ -21,6 +27,7 @@ data class ToolConfig(val config: Config) {
val keyNameArg = parser.accepts("keyName").withRequiredArg() val keyNameArg = parser.accepts("keyName").withRequiredArg()
val keyGroupArg = parser.accepts("keyGroup").withRequiredArg() val keyGroupArg = parser.accepts("keyGroup").withRequiredArg()
val keySpecifierArg = parser.accepts("keySpecifier").withRequiredArg() val keySpecifierArg = parser.accepts("keySpecifier").withRequiredArg()
val overwriteArg = parser.accepts("overwriteKey").withOptionalArg()
val options = try { val options = try {
parser.parse(*args) parser.parse(*args)
} catch (e: Exception) { } catch (e: Exception) {
@ -29,21 +36,27 @@ data class ToolConfig(val config: Config) {
exitProcess(1) exitProcess(1)
} }
val sourcePath = options.valueOf(sourcePathArg)!! val sourcePath = options.valueOf(sourcePathArg)
val publicKeyOutputPath = options.valueOf(publicKeyOutputPathArg)!! val mode = options.valueOf(modeArg)
val signatureOutputPath = options.valueOf(signatureOutputPathArg)!! val publicKeyOutputPath = options.valueOf(publicKeyOutputPathArg)
val signatureOutputPath = options.valueOf(signatureOutputPathArg)
val baseConfig = if (options.hasArgument(configPathArg)) { val baseConfig = if (options.hasArgument(configPathArg)) {
val configPath = Paths.get(options.valueOf(configPathArg)!!) val configPath = Paths.get(options.valueOf(configPathArg)!!)
require(Files.exists(configPath)) { "Config file $sourcePath not found" } require(Files.exists(configPath)) { "Config file $configPath not found" }
ConfigFactory.parseFile(configPath.toFile()) ConfigFactory.parseFile(configPath.toFile())
} else { } else {
ConfigFactory.parseResources(ToolConfig::class.java, "sgxtool.cfg") ConfigFactory.parseResources(ToolConfig::class.java, "sgxtool.cfg")
} }
val profile = options.valueOf(profileArg)!!.toLowerCase() val profile = options.valueOf(profileArg)!!.toLowerCase()
val overrideMap = mutableMapOf("profile" to profile, "sourcePath" to sourcePath, val overrideMap = mutableMapOf(
"publicKeyOutputPath" to publicKeyOutputPath, "mode" to mode,
"signatureOutputPath" to signatureOutputPath) "profile" to profile
)
if (sourcePath != null) overrideMap["sourcePath"] = sourcePath
if (publicKeyOutputPath != null) overrideMap["publicKeyOutputPath"] = publicKeyOutputPath
if (signatureOutputPath != null) overrideMap["signatureOutputPath"] = signatureOutputPath
overrideMap["overwriteKey"] = options.has(overwriteArg).toString()
if (options.hasArgument(deviceArg)) { if (options.hasArgument(deviceArg)) {
overrideMap["$profile.device"] = options.valueOf(deviceArg) overrideMap["$profile.device"] = options.valueOf(deviceArg)
} }
@ -61,18 +74,33 @@ data class ToolConfig(val config: Config) {
final!! final!!
}()) }())
val mode: Mode by config
val profile: String by config val profile: String by config
val profileConfig: Config get() = config.getConfig(profile) val profileConfig: Config get() = config.getConfig(profile)
val device: String by profileConfig val device: String by profileConfig
val keyName: String by profileConfig val keyName: String by profileConfig
val keyGroup: String by profileConfig val keyGroup: String by profileConfig
val keySpecifier: String by profileConfig val keySpecifier: String by profileConfig
val sourcePath: Path by config val sourcePath: Path? by config
val publicKeyOutputPath: Path by config val publicKeyOutputPath: Path? by config
val signatureOutputPath: Path by config val signatureOutputPath: Path? by config
val overwriteKey: Boolean by config
init {
when (mode) {
Mode.Sign -> {
requireNotNull(sourcePath)
requireNotNull(signatureOutputPath)
requireNotNull(publicKeyOutputPath)
}
Mode.GenerateKey -> {
}
}
}
override fun toString(): String { override fun toString(): String {
val sb = StringBuilder() val sb = StringBuilder()
sb.append("mode: $mode\n")
sb.append("profile: $profile\n") sb.append("profile: $profile\n")
sb.append("device: $device\n") sb.append("device: $device\n")
sb.append("keyName: $keyName\n") sb.append("keyName: $keyName\n")

View File

@ -0,0 +1,150 @@
package com.r3cev.sgx.hsmtool
import CryptoServerAPI.CryptoServerException
import CryptoServerCXI.CryptoServerCXI
import CryptoServerJCE.CryptoServerProvider
import com.r3cev.sgx.config.Mode
import com.r3cev.sgx.config.ToolConfig
import com.r3cev.sgx.utils.HsmErrors
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.FileWriter
import java.math.BigInteger
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.security.*
import java.security.spec.X509EncodedKeySpec
fun sign(config: ToolConfig) {
require(Files.exists(config.sourcePath)) { "Input ${config.sourcePath} not found" }
connectAndAuthenticate(config) { provider ->
val keyStore = KeyStore.getInstance("CryptoServer", provider)
keyStore.load(null, null)
val aliases = keyStore.aliases().toList()
println(aliases)
val sgxKey = keyStore.getKey(config.keyName, null) as PrivateKey
val data = Files.readAllBytes(config.sourcePath)
val signer = Signature.getInstance("SHA256WithRSA", provider)
signer.initSign(sgxKey)
signer.update(data)
val signature = signer.sign()
val javaFactory = KeyFactory.getInstance("RSA")
val sgxPub = keyStore.getCertificate(config.keyName).publicKey
val sgxPubReImport = javaFactory.generatePublic(X509EncodedKeySpec(sgxPub.encoded))
val verify = Signature.getInstance("SHA256WithRSA")
verify.initVerify(sgxPubReImport)
verify.update(data)
require(verify.verify(signature)) { "Signature didn't independently verify" }
System.setProperty("line.separator", "\n") // Ensure UNIX line endings in PEM files
println("Writing signature")
saveSignatureAsFile(signature, config.signatureOutputPath!!)
println("Writing public key")
savePublicKeyAsPEMFile(sgxPubReImport, config.publicKeyOutputPath!!)
}
}
fun generateKey(config: ToolConfig) {
val generateFlag = if (config.overwriteKey) {
println("!!! WARNING: OVERWRITING KEY NAMED ${config.keyName} !!!")
CryptoServerCXI.FLAG_OVERWRITE
} else {
0
}
connectAndAuthenticate(config) { provider ->
val keyAttributes = CryptoServerCXI.KeyAttributes()
keyAttributes.apply {
algo = CryptoServerCXI.KEY_ALGO_RSA
size = 3072
setExponent(BigInteger.valueOf(0x03))
group = config.keyGroup
specifier = config.keySpecifier.toInt()
export = 0 // deny export
name = config.keyName
}
println("Generating key...")
// This should be CryptoServerCXI.MECH_KEYGEN_FIPS186_4_PRIME, however FIPS doesn't support exponent 3
val mechanismFlag = CryptoServerCXI.MECH_RND_REAL or CryptoServerCXI.MECH_KEYGEN_ANSI_PRIME
provider.cryptoServer.generateKey(generateFlag, keyAttributes, mechanismFlag)
}
}
fun connectAndAuthenticate(config: ToolConfig, block: (CryptoServerProvider) -> Unit) {
println("Connect to ${config.device}")
val provider = createProvider(config.device, config.keyGroup, config.keySpecifier)
try {
while (true) {
print("Enter User Name: ")
val user = readLine()
println("Login user: $user")
provider.loginSign(user, ":cs2:cyb:USB0", null)
val auth = provider.cryptoServer.authState
if ((auth and 0x0000000F) >= 0x00000002) {
println("Auth Sufficient")
break
}
println("Need more permissions. Add extra login")
}
block(provider)
} finally {
provider.logoff()
}
}
fun main(args: Array<String>) {
println("SGX Tool started")
val config = ToolConfig(args)
println(config)
try {
when (config.mode) {
Mode.Sign -> sign(config)
Mode.GenerateKey -> generateKey(config)
}
println("Done!")
} catch (exception: Throwable) {
// Try to decode the error code
val crypto = exception as? CryptoServerException ?: exception.cause as? CryptoServerException
if (crypto != null) {
throw Exception(HsmErrors.errors[crypto.ErrorCode], exception)
} else {
throw exception
}
}
}
private fun saveSignatureAsFile(signature: ByteArray, filename: Path) {
Files.write(filename, signature, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
}
private fun savePublicKeyAsPEMFile(key: PublicKey, filename: Path) {
FileWriter(filename.toFile()).use {
JcaPEMWriter(it).use {
it.writeObject(key)
}
}
}
private fun createProvider(device: String, keyGroup: String, keySpecifier: String): CryptoServerProvider {
val cfgBuffer = ByteArrayOutputStream()
val writer = cfgBuffer.writer(Charsets.UTF_8)
writer.write("Device = $device\n")
writer.write("ConnectionTimeout = 3000\n")
writer.write("Timeout = 30000\n")
writer.write("EndSessionOnShutdown = 1\n")
writer.write("KeepSessionAlive = 0\n")
writer.write("KeyGroup = $keyGroup\n")
writer.write("KeySpecifier = $keySpecifier\n")
writer.write("StoreKeysExternal = false\n")
writer.close()
val cfg = ByteArrayInputStream(cfgBuffer.toByteArray())
cfgBuffer.close()
val provider = CryptoServerProvider(cfg)
cfg.close()
return provider
}

View File

@ -0,0 +1,22 @@
package com.r3cev.sgx.utils
import java.util.*
object HsmErrors {
val errors: Map<Int, String> by lazy(HsmErrors::load)
fun load(): Map<Int, String> {
val errors = HashMap<Int, String>()
val hsmErrorsStream = HsmErrors::class.java.getResourceAsStream("hsm_errors")
hsmErrorsStream.bufferedReader().lines().reduce(null) { previous, current ->
if (previous == null) {
current
} else {
errors[java.lang.Long.decode(previous).toInt()] = current
null
}
}
return errors
}
}

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,6 @@ set_target_properties(edger8r PROPERTIES IMPORTED_LOCATION ${SGX_LIBRARY_PATH}/s
set(GENERATED_EDL_FILES ${GENERATED_RPC_DIR}/java_t.c ${GENERATED_RPC_DIR}/java_t.h ${GENERATED_RPC_DIR}/java_u.c ${GENERATED_RPC_DIR}/java_u.h) set(GENERATED_EDL_FILES ${GENERATED_RPC_DIR}/java_t.c ${GENERATED_RPC_DIR}/java_t.h ${GENERATED_RPC_DIR}/java_u.c ${GENERATED_RPC_DIR}/java_u.h)
set(GENERATED_EDL_FILES ${GENERATED_EDL_FILES} PARENT_SCOPE) set(GENERATED_EDL_FILES ${GENERATED_EDL_FILES} PARENT_SCOPE)
message(STATUS FFS "${GENERATED_EDL_FILES}")
add_custom_command( add_custom_command(
OUTPUT ${GENERATED_EDL_FILES} OUTPUT ${GENERATED_EDL_FILES}
COMMAND edger8r --search-path ${PROJECT_SOURCE_DIR}/rpc --search-path ${SGX_SDK_INCLUDE} --trusted-dir ${GENERATED_RPC_DIR} --untrusted-dir ${GENERATED_RPC_DIR} java.edl COMMAND edger8r --search-path ${PROJECT_SOURCE_DIR}/rpc --search-path ${SGX_SDK_INCLUDE} --trusted-dir ${GENERATED_RPC_DIR} --untrusted-dir ${GENERATED_RPC_DIR} java.edl

View File

@ -147,7 +147,7 @@ set_target_properties(sgx_sign PROPERTIES IMPORTED_LOCATION ${SGX_SIGN_TOOL})
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB} OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB}
COMMAND sgx_sign sign -key ${PRIVATE_KEY_NAME} -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml COMMAND sgx_sign sign -key ${PRIVATE_KEY_NAME} -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} ${PRIVATE_KEY_NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} ${PRIVATE_KEY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml
) )
add_custom_target(enclave DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB}) add_custom_target(enclave DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB})

View File

@ -4,15 +4,25 @@ set(SGX_SDK ${CMAKE_CURRENT_SOURCE_DIR}/../linux-sgx)
set(SGX_LIBRARY_PATH ${SGX_SDK}/build/linux) set(SGX_LIBRARY_PATH ${SGX_SDK}/build/linux)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fpie -fstack-protector") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fpie -fstack-protector")
set(SGX_SIGN_TOOL ${SGX_SDK}/build/linux/sgx_sign) set(SGX_SIGN_TOOL ${SGX_SDK}/build/linux/sgx_sign)
set(ENCLAVE_UNSIGNED_OUTPUT_LIB noop_enclave.so) set(ENCLAVE_UNSIGNED noop_enclave.unsigned.so)
set(ENCLAVE_BLOB_TO_SIGN noop_enclave_blob_to_sign.bin) set(ENCLAVE_BLOB_TO_SIGN noop_enclave_blob_to_sign.bin)
set(ENCLAVE_SIGNED_OUTPUT_LIB noop_enclave.signed.so) set(ENCLAVE_SIGNED_OPENSSL noop_enclave.signed.openssl.so)
set(PRIVATE_KEY_NAME selfsigning.pem) set(ENCLAVE_SIGNED_HSM noop_enclave.signed.hsm.so)
set(PUBLIC_KEY_NAME selfsigning.public.pem) set(ENCLAVE_SIGNATURE_OPENSSL noop_enclave.signature.openssl.sha256)
set(ENCLAVE_SIGNATURE_HSM noop_enclave.signature.hsm.sha256)
set(ENCLAVE_SIGSTRUCT_OPENSSL noop_enclave.sigstruct.openssl.bin)
set(ENCLAVE_SIGSTRUCT_HSM noop_enclave.sigstruct.hsm.bin)
set(ENCLAVE_SIGSTRUCT_PRETTY_OPENSSL noop_enclave.sigstruct-pretty.openssl.txt)
set(ENCLAVE_SIGSTRUCT_PRETTY_HSM noop_enclave.sigstruct-pretty.hsm.txt)
set(PRIVATE_KEY_NAME_OPENSSL selfsigning.pem)
set(PUBLIC_KEY_NAME_OPENSSL selfsigning.public.pem)
set(PUBLIC_KEY_NAME_HSM hsm.public.pem)
set(SIGN_HELPER ${PROJECT_SOURCE_DIR}/sign_helper/sign_helper)
set(HSM_SGX_TOOL ${PROJECT_SOURCE_DIR}/../hsm-tool/build/libs/sgx-jvm/hsm-tool-1.0-SNAPSHOT.jar)
set(NOOP_ENCLAVE noop_enclave_objects) set(NOOP_ENCLAVE noop_enclave_objects)
set(SGX_SDK_INCLUDE ${SGX_SDK}/common/inc) set(SGX_SDK_INCLUDE ${SGX_SDK}/common/inc)
set(GENERATED_RPC_DIR ${CMAKE_CURRENT_BINARY_DIR}/rpc) set(GENERATED_RPC_DIR ${CMAKE_CURRENT_BINARY_DIR}/rpc)
set(ENCLAVE_SIGNATURE noop_enclave.signature.sha256)
set(GENERATED_EDL_FILES ${GENERATED_RPC_DIR}/empty_t.c ${GENERATED_RPC_DIR}/empty_t.h ${GENERATED_RPC_DIR}/empty_u.c ${GENERATED_RPC_DIR}/empty_u.h) set(GENERATED_EDL_FILES ${GENERATED_RPC_DIR}/empty_t.c ${GENERATED_RPC_DIR}/empty_t.h ${GENERATED_RPC_DIR}/empty_u.c ${GENERATED_RPC_DIR}/empty_u.h)
add_custom_command( add_custom_command(
@ -35,10 +45,14 @@ target_compile_options(${NOOP_ENCLAVE} PUBLIC -nostdinc)
add_executable(edger8r IMPORTED) add_executable(edger8r IMPORTED)
set_target_properties(edger8r PROPERTIES IMPORTED_LOCATION ${SGX_LIBRARY_PATH}/sgx_edger8r) set_target_properties(edger8r PROPERTIES IMPORTED_LOCATION ${SGX_LIBRARY_PATH}/sgx_edger8r)
set(SGX_USE_HARDWARE FALSE)
if(SGX_USE_HARDWARE) if(SGX_USE_HARDWARE)
set(URTS_LIB "sgx_urts")
set(TRTS_LIB "sgx_trts") set(TRTS_LIB "sgx_trts")
set(SGX_SERVICE_LIB "sgx_tservice") set(SGX_SERVICE_LIB "sgx_tservice")
else() else()
set(URTS_LIB "sgx_urts_sim")
set(TRTS_LIB "sgx_trts_sim") set(TRTS_LIB "sgx_trts_sim")
set(SGX_SERVICE_LIB "sgx_tservice_sim") set(SGX_SERVICE_LIB "sgx_tservice_sim")
endif() endif()
@ -69,43 +83,100 @@ set(ENCLAVE_LINKER_FLAGS
) )
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED}
COMMAND ${CMAKE_CXX_COMPILER} -o ${ENCLAVE_UNSIGNED_OUTPUT_LIB} ${ENCLAVE_LINKER_FLAGS} COMMAND ${CMAKE_CXX_COMPILER} -o ${ENCLAVE_UNSIGNED} ${ENCLAVE_LINKER_FLAGS}
DEPENDS ${NOOP_ENCLAVE} ${SGX_LIBRARY_PATH} DEPENDS ${NOOP_ENCLAVE} ${SGX_LIBRARY_PATH}
) )
add_executable(sgx_sign IMPORTED) add_executable(sgx_sign IMPORTED)
set_target_properties(sgx_sign PROPERTIES IMPORTED_LOCATION ${SGX_SIGN_TOOL}) set_target_properties(sgx_sign PROPERTIES IMPORTED_LOCATION ${SGX_SIGN_TOOL})
# add_custom_command(
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB}
# COMMAND sgx_sign sign -key ${CMAKE_CURRENT_SOURCE_DIR}/${PRIVATE_KEY_NAME} -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml
# DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} ${CMAKE_CURRENT_SOURCE_DIR}/${PRIVATE_KEY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml
# )
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN}
COMMAND sgx_sign gendata -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml COMMAND sgx_sign gendata -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED}
) )
# TODO: replace with getting the pubkey from HSM # outputs the unsigned enclave and the blob to sign
add_custom_target(unsigned DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED} ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN})
# OPENSSL ENCLAVE
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME} OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_OPENSSL}
COMMAND openssl rsa -in ${CMAKE_CURRENT_SOURCE_DIR}/${PRIVATE_KEY_NAME} -pubout -out ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME} COMMAND openssl rsa -in ${CMAKE_CURRENT_SOURCE_DIR}/${PRIVATE_KEY_NAME_OPENSSL} -pubout -out ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_OPENSSL}
) )
# TODO: replace with signing on HSM
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE} OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_OPENSSL}
COMMAND openssl dgst -sha256 -sign ${CMAKE_CURRENT_SOURCE_DIR}/${PRIVATE_KEY_NAME} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE} ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} COMMAND openssl dgst -sha256 -sign ${CMAKE_CURRENT_SOURCE_DIR}/${PRIVATE_KEY_NAME_OPENSSL} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_OPENSSL} ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN}
) )
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB} OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OPENSSL}
COMMAND sgx_sign catsig -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED_OUTPUT_LIB} -key ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME} -sig ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE} -unsigned ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB} COMMAND sgx_sign catsig -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED} -key ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_OPENSSL} -sig ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_OPENSSL} -unsigned ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OPENSSL}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE} ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_OPENSSL} ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_OPENSSL}
) )
add_custom_target(noop-enclave ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OUTPUT_LIB}) add_custom_target(signed-openssl DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OPENSSL})
# /OPENSSL ENCLAVE
# HSM ENCLAVE
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_HSM} ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_HSM}
COMMAND java -jar ${HSM_SGX_TOOL} --mode=Sign --source=${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} --pubkey=${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_HSM} --signature=${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_HSM} --profile=\${PROFILE}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN}
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_HSM}
COMMAND sgx_sign catsig -enclave ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_UNSIGNED} -key ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_HSM} -sig ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_HSM} -unsigned ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_BLOB_TO_SIGN} -config ${CMAKE_CURRENT_SOURCE_DIR}/enclave.xml -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_HSM}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNATURE_HSM} ${CMAKE_CURRENT_BINARY_DIR}/${PUBLIC_KEY_NAME_HSM}
)
add_custom_target(signed-hsm DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_HSM})
# /HSM ENCLAVE
# HSM KEY
add_custom_command(
OUTPUT __generate-key-hsm-dummy__
COMMAND java -jar ${HSM_SGX_TOOL} --mode=GenerateKey --profile=\${PROFILE} \$\(shell bash -c '[[ \${OVERWRITE} = "true" ]] && echo "--overwriteKey"' \)
)
add_custom_target(generate-key-hsm DEPENDS __generate-key-hsm-dummy__)
# /HSM KEY
# OPENSSL SIGSTRUCT
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_OPENSSL}
COMMAND ${SIGN_HELPER} get-css -in ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OPENSSL} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_OPENSSL}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_OPENSSL}
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_PRETTY_OPENSSL}
COMMAND ${SIGN_HELPER} print-css -in ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_OPENSSL} > ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_PRETTY_OPENSSL}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_OPENSSL}
)
add_custom_target(sigstruct-openssl DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_OPENSSL} ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_PRETTY_OPENSSL})
# /OPENSSL SIGSTRUCT
# HSM SIGSTRUCT
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_HSM}
COMMAND ${SIGN_HELPER} get-css -in ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_HSM} -out ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_HSM}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGNED_HSM}
)
add_custom_target(sigstruct-hsm DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ENCLAVE_SIGSTRUCT_HSM})
# /HSM SIGSTRUCT
# test
add_library(urtslib SHARED IMPORTED)
set_target_properties(urtslib PROPERTIES IMPORTED_LOCATION ${SGX_LIBRARY_PATH}/lib${URTS_LIB}.so)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
add_executable(noop_test src/test.cpp ${GENERATED_RPC_DIR}/empty_u.c)
target_include_directories(noop_test PUBLIC ${SGX_SDK_INCLUDE} ${GENERATED_RPC_DIR})
target_link_libraries(noop_test urtslib Threads::Threads)

View File

@ -1,5 +1,7 @@
.PHONY: all .PHONY: all
all: build/noop-enclave.so all: build/Makefile
$(MAKE) -C $(<D) help
exit 1
build: build:
mkdir -p build mkdir -p build
@ -7,8 +9,8 @@ build:
build/Makefile: | build build/Makefile: | build
cd build/ && cmake .. cd build/ && cmake ..
build/noop-enclave.so: build/Makefile %: build/Makefile
$(MAKE) -C $(<D) VERBOSE=1 $(MAKE) -C $(<D) $@
clean: clean:
rm -rf build rm -rf build

View File

@ -0,0 +1,26 @@
What is this?
=============
This project contains a noop enclave with a single ECALL that does
nothing. Its purpose is to demonstrate our ability to create a signed
enclave and to test the signature process through an HSM.
How to run
==========
The following Makefile targets execute different steps in the signing process and output into build/
`make unsigned` will build the unsigned enclave (noop\_enclave.unsigned.so).
The following targets use OpenSSL instead of the HSM:
`make signed-openssl` will sign the unsigned enclave with openssl using selfsigning.pem (noop\_enclave.signed.openssl.so).
`make sigstruct-openssl` will extract the SIGSTRUCT into a blob as well as a pretty printed txt from the openssl signed enclave (noop\_enclave.sigstruct.openssl.bin, noop\_enclave.sigstruct-pretty.openssl.txt).
The following targets use the HSM. They require an extra `PROFILE=[dev|prod]` argument to indicate whether to use a local HSM simulator or the real thing.
`make generate-key-hsm PROFILE=[dev|prod] [OVERWRITE=true]` will generate a fresh key for the profile. By default this will not overwrite an existing key, for that pass in MODE=overwrite.
`make signed-hsm PROFILE=[dev|prod]` will sign the unsigned enclave with the HSM. This target requires authentication (noop\_enclave.signed.hsm.so).
`make sigstruct-hsm PROFILE=[dev|prod]` will extract the SIGSTRUCT into a blob as well as a pretty printed txt from the HSM signed enclave (noop\_enclave.sigstruct.hsm.bin, noop\_enclave.sigstruct-pretty.hsm.txt).
`make noop_test` will create a test binary that loads an enclave and runs the noop ECALL inside it. For example:
`./build/noop_test ./build/noop_enclave.signed.openssl.so`
will run the noop ECALL using the openssl signed enclave.

Binary file not shown.

View File

@ -0,0 +1,144 @@
#include "empty_u.h"
#include <sgx_urts.h>
#include <sgx.h>
#include <cstdlib>
#include <cstdio>
typedef struct {
sgx_status_t err;
const char *message;
const char *suggestion;
} sgx_errlist_t;
/* Error code returned by sgx_create_enclave */
static sgx_errlist_t sgx_errlist[] = {
{
SGX_ERROR_UNEXPECTED,
"Unexpected error occurred.",
NULL
},
{
SGX_ERROR_INVALID_PARAMETER,
"Invalid parameter.",
NULL
},
{
SGX_ERROR_OUT_OF_MEMORY,
"Out of memory.",
NULL
},
{
SGX_ERROR_ENCLAVE_LOST,
"Power transition occurred.",
"Please refer to the sample \"PowerTransition\" for details."
},
{
SGX_ERROR_INVALID_ENCLAVE,
"Invalid enclave image.",
NULL
},
{
SGX_ERROR_INVALID_ENCLAVE_ID,
"Invalid enclave identification.",
NULL
},
{
SGX_ERROR_INVALID_SIGNATURE,
"Invalid enclave signature.",
NULL
},
{
SGX_ERROR_OUT_OF_EPC,
"Out of EPC memory.",
NULL
},
{
SGX_ERROR_NO_DEVICE,
"Invalid SGX device.",
"Please make sure SGX module is enabled in the BIOS, and install SGX driver afterwards."
},
{
SGX_ERROR_MEMORY_MAP_CONFLICT,
"Memory map conflicted.",
NULL
},
{
SGX_ERROR_INVALID_METADATA,
"Invalid enclave metadata.",
NULL
},
{
SGX_ERROR_DEVICE_BUSY,
"SGX device was busy.",
NULL
},
{
SGX_ERROR_INVALID_VERSION,
"Enclave version was invalid.",
NULL
},
{
SGX_ERROR_INVALID_ATTRIBUTE,
"Enclave was not authorized.",
NULL
},
{
SGX_ERROR_ENCLAVE_FILE_ACCESS,
"Can't open enclave file.",
NULL
},
};
/* Check error conditions for loading enclave */
void print_error_message(sgx_status_t ret)
{
size_t idx = 0;
size_t ttl = sizeof sgx_errlist/sizeof sgx_errlist[0];
for (idx = 0; idx < ttl; idx++) {
if(ret == sgx_errlist[idx].err) {
if(NULL != sgx_errlist[idx].suggestion)
printf("Info: %s\n", sgx_errlist[idx].suggestion);
printf("Error: %s\n", sgx_errlist[idx].message);
break;
}
}
if (idx == ttl)
printf("Error: Unexpected error occurred.\n");
}
inline bool check_sgx_return_value(sgx_status_t ret)
{
if (ret == SGX_SUCCESS)
{
return true;
}
else
{
print_error_message(ret);
return false;
}
}
int main(int argc, char **argv) {
if (argc != 2) {
puts("Usage: <binary> <signed.enclave.so>");
return 1;
}
const char *enclave_path = argv[1];
sgx_launch_token_t token = {0};
sgx_enclave_id_t enclave_id = {0};
int updated = 0;
if (false == check_sgx_return_value(sgx_create_enclave(enclave_path, SGX_DEBUG_FLAG, &token, &updated, &enclave_id, NULL))) {
return 1;
}
if (false == check_sgx_return_value(noop(enclave_id))) {
return 1;
}
return 0;
}

View File

@ -1,91 +0,0 @@
package com.r3cev.sgx.signtool
import CryptoServerJCE.CryptoServerProvider
import com.r3cev.sgx.config.ToolConfig
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.FileWriter
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.security.*
import java.security.spec.X509EncodedKeySpec
fun main(args: Array<String>) {
println("SGX Tool started")
val config = ToolConfig(args)
require(Files.exists(config.sourcePath)) { "Input ${config.sourcePath} not found" }
println(config)
println("Connect to ${config.device}")
val provider = createProvider(config.device, config.keyGroup, config.keySpecifier)
while (true) {
print("Enter User Name: ")
val user = readLine()
println("Login user: $user")
provider.loginSign(user, ":cs2:cyb:USB0", null)
val auth = provider.cryptoServer.authState
if ((auth and 0x0000000F) >= 0x00000002) {
println("Auth Sufficient")
break
}
println("Need more permissions. Add extra login")
}
val keyStore = KeyStore.getInstance("CryptoServer", provider)
keyStore.load(null, null)
val aliases = keyStore.aliases().toList()
println(aliases)
val sgxKey = keyStore.getKey(config.keyName, null) as PrivateKey
val data = Files.readAllBytes(config.sourcePath)
val signer = Signature.getInstance("SHA256WithRSA", provider)
signer.initSign(sgxKey)
signer.update(data)
val signature = signer.sign()
val javaFactory = KeyFactory.getInstance("RSA")
val sgxPub = keyStore.getCertificate(config.keyName).publicKey
val sgxPubReImport = javaFactory.generatePublic(X509EncodedKeySpec(sgxPub.encoded))
val verify = Signature.getInstance("SHA256WithRSA")
verify.initVerify(sgxPubReImport)
verify.update(data)
require(verify.verify(signature)) { "Signature didn't independently verify" }
System.setProperty("line.separator", "\n") // Ensure UNIX line endings in PEM files
saveSignatureAsFile(signature, config.signatureOutputPath)
savePublicKeyAsPEMFile(sgxPubReImport, config.publicKeyOutputPath)
provider.logoff()
}
private fun saveSignatureAsFile(signature: ByteArray, filename: Path) {
Files.write(filename, signature, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)
}
private fun savePublicKeyAsPEMFile(key: PublicKey, filename: Path) {
FileWriter(filename.toFile()).use {
JcaPEMWriter(it).use {
it.writeObject(key)
}
}
}
private fun createProvider(device: String, keyGroup: String, keySpecifier: String): CryptoServerProvider {
val cfgBuffer = ByteArrayOutputStream()
val writer = cfgBuffer.writer(Charsets.UTF_8)
writer.write("Device = $device\n")
writer.write("ConnectionTimeout = 3000\n")
writer.write("Timeout = 30000\n")
writer.write("EndSessionOnShutdown = 1\n")
writer.write("KeepSessionAlive = 0\n")
writer.write("KeyGroup = $keyGroup\n")
writer.write("KeySpecifier = $keySpecifier\n")
writer.write("StoreKeysExternal = false\n")
writer.close()
val cfg = ByteArrayInputStream(cfgBuffer.toByteArray())
cfgBuffer.close()
val provider = CryptoServerProvider(cfg)
cfg.close()
return provider
}