提交 0f0f632f 编写于 作者: M Matt Witherspoon

applesedemo - Demo application showing how to use the Apple Secure Enclave to...

applesedemo - Demo application showing how to use the Apple Secure Enclave to sign (and then recover) via eccr1 keys
上级 24844228
......@@ -2,4 +2,5 @@ add_subdirectory( eosd )
add_subdirectory( eosc )
add_subdirectory( eos-walletd )
add_subdirectory( launcher )
add_subdirectory( codegen )
\ No newline at end of file
add_subdirectory( codegen )
add_subdirectory( applesedemo )
\ No newline at end of file
if(APPLE)
add_executable( applesedemo main.cpp r1_signature_compactor.cpp )
target_link_libraries( applesedemo
PRIVATE fc ${PLATFORM_SPECIFIC_LIBS}
)
set_target_properties(applesedemo PROPERTIES LINK_FLAGS "-framework security -framework corefoundation")
#Demostration of signing automatically during build; you will need to change parameters for your signing credentials
#[[
add_custom_command(TARGET applesedemo POST_BUILD
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/sign.sh C5139C2C4D7FA071EFBFD86CE44B652631C9376A 5A4683969Z.one.block.applesedemo /Users/spoon/Library/MobileDevice/Provisioning\ Profiles/95813ad5-e880-432f-85c6-ade3b3298392.provisionprofile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)
]]
install( TARGETS
applesedemo
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
endif(APPLE)
\ No newline at end of file
#include <fc/crypto/elliptic_r1.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/crypto/base58.hpp>
#include <fc/crypto/signature.hpp>
#include <fc/crypto/public_key.hpp>
#include <boost/program_options.hpp>
#include <Security/Security.h>
#include <array>
#include "r1_signature_compactor.hpp"
#include <fc/crypto/public_key.hpp>
#include <fc/crypto/private_key.hpp>
#include <fc/crypto/signature.hpp>
namespace po = boost::program_options;
using namespace std;
using namespace fc::crypto::r1;
//get a copy of our key info, should it exist in the keychain
CFDictionaryRef CopyOurKeyInfo() {
const void* keyAttrKeys[] = {
kSecClass,
kSecAttrKeyClass,
kSecAttrApplicationTag,
kSecReturnAttributes,
kSecReturnRef
};
const void* keyAttrValues[] = {
kSecClassKey,
kSecAttrKeyClassPrivate,
CFSTR("one.block.applesedemo"),
kCFBooleanTrue,
kCFBooleanTrue
};
CFDictionaryRef keyAttrDic = CFDictionaryCreate(nullptr, keyAttrKeys, keyAttrValues, sizeof(keyAttrKeys)/sizeof(keyAttrKeys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryRef attributes = nullptr;
OSStatus ret = SecItemCopyMatching(keyAttrDic, (CFTypeRef*)&attributes);
CFRelease(keyAttrDic);
return attributes;
}
SecKeyRef CopyOurKey() {
CFDictionaryRef attributes = CopyOurKeyInfo();
if(!attributes)
return nullptr;
SecKeyRef key = (SecKeyRef)CFDictionaryGetValue(attributes, kSecValueRef);
if(key)
CFRetain(key);
CFRelease(attributes);
return key;
}
public_key_data get_compressed_pub_for_key(SecKeyRef key) {
SecKeyRef pubkey = SecKeyCopyPublicKey(key);
CFErrorRef error = nullptr;
CFDataRef keyrep = SecKeyCopyExternalRepresentation(pubkey, &error);
assert(CFDataGetLength(keyrep) == 65);
public_key_data pub_key_data;
const UInt8* cfdata = CFDataGetBytePtr(keyrep);
memcpy(pub_key_data.data+1, cfdata+1, 32);
pub_key_data.data[0] = 0x02 + (cfdata[64]&1);
CFRelease(keyrep);
CFRelease(pubkey);
return pub_key_data;
}
void print_pub_for_key(SecKeyRef key) {
fc::crypto::checksummed_data<fc::crypto::r1::public_key_data> pub_wrapper;
pub_wrapper.data = get_compressed_pub_for_key(key);
pub_wrapper.check = fc::crypto::checksummed_data<fc::crypto::r1::public_key_data>::calculate_checksum(pub_wrapper.data, "R1");
std::vector<char> checksummed = fc::raw::pack(pub_wrapper);
cout << "public_key(EOSR1" << fc::to_base58(checksummed.data(), checksummed.size()) << ")" << endl;
}
void print_attributes() {
CFDictionaryRef key_info = CopyOurKeyInfo();
if(!key_info)
cout << "No key currently in SE" << endl;
else {
CFShow(key_info);
CFRelease(key_info);
}
SecKeyRef key = CopyOurKey();
print_pub_for_key(key);
CFRelease(key);
}
void remove() {
SecKeyRef key = CopyOurKey();
if(!key) {
cout << "No key current in SE" << endl;
return;
}
CFRelease(key);
//hmmm. previously I was trying to delete based on the key reference; no such luck
const void* deleteKeys[] = {
kSecClass,
kSecAttrApplicationTag,
};
const void* deleteValues[] = {
kSecClassKey,
CFSTR("one.block.applesedemo"),
};
CFDictionaryRef deleteDic = CFDictionaryCreate(nullptr, deleteKeys, deleteValues, sizeof(deleteKeys)/sizeof(deleteKeys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
OSStatus ret = SecItemDelete(deleteDic);
if(!ret)
cout << "Successfully removed key" << endl;
else
cout << "Error removing key " << ret << endl;
CFRelease(deleteDic);
}
void create(SecAccessControlCreateFlags flags) {
SecKeyRef key = CopyOurKey();
if(key) {
cout << "Already have a key in SE; not creating more" << endl;
CFRelease(key);
return;
}
SecAccessControlRef accessControlRef = SecAccessControlCreateWithFlags(nullptr, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, flags | kSecAccessControlPrivateKeyUsage, nullptr);
int keySizeValue = 256;
CFNumberRef keySizeNumber = CFNumberCreate(NULL, kCFNumberIntType, &keySizeValue);
const void* keyAttrKeys[] = {
kSecAttrIsPermanent,
kSecAttrApplicationTag,
kSecAttrAccessControl
};
const void* keyAttrValues[] = {
kCFBooleanTrue,
CFSTR("one.block.applesedemo"),
accessControlRef
};
CFDictionaryRef keyAttrDic = CFDictionaryCreate(NULL, keyAttrKeys, keyAttrValues, sizeof(keyAttrKeys)/sizeof(keyAttrKeys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
const void* attrKeys[] = {
kSecAttrKeyType,
kSecAttrKeySizeInBits,
kSecAttrTokenID,
kSecPrivateKeyAttrs
};
const void* atrrValues[] = {
kSecAttrKeyTypeECSECPrimeRandom,
keySizeNumber,
kSecAttrTokenIDSecureEnclave,
keyAttrDic
};
CFDictionaryRef attributesDic = CFDictionaryCreate(NULL, attrKeys, atrrValues, sizeof(attrKeys)/sizeof(atrrValues[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey(attributesDic, &error);
if(error) {
cout << "Failed" << endl;
CFShow(error);
}
else {
cout << "Successfully created" << endl;
print_pub_for_key(privateKey);
CFRelease(privateKey);
}
CFRelease(attributesDic);
CFRelease(keyAttrDic);
CFRelease(keySizeNumber);
CFRelease(accessControlRef);
}
void sign(const string& hex) {
SecKeyRef key = CopyOurKey();
if(!key) {
cout << "No key currently in SE" << endl;
return;
}
fc::ecdsa_sig sig = ECDSA_SIG_new();
const UInt8* der_bytes = nullptr;
fc::sha256 digest(hex);
CFErrorRef error = nullptr;
fc::crypto::r1::compact_signature compacted;
public_key_data pub;
fc::crypto::checksummed_data<fc::crypto::r1::compact_signature> signature_wrapper;
std::vector<char> checksummed;
std::string blah;
CFDataRef digestData = CFDataCreateWithBytesNoCopy(nullptr, (UInt8*)digest.data(), digest.data_size(), kCFAllocatorNull);
//the X9.62 representation of r & s must be exactly 32 bytes or otherwise the "canonical" check fails
while(true) {
CFDataRef signature = SecKeyCreateSignature(key, kSecKeyAlgorithmECDSASignatureDigestX962SHA256, digestData, &error);
if(error) {
cout << "Failed to sign" << endl;
CFShow(error);
goto err;
}
der_bytes = CFDataGetBytePtr(signature);
assert(der_bytes[0] == 0x30);
assert(der_bytes[2] == 0x02);
assert(der_bytes[4+der_bytes[3]] == 0x02);
UInt8 rLen, sLen;
rLen = der_bytes[3];
sLen = der_bytes[5+32];
CFRelease(signature);
if(rLen == 32 && sLen == 32)
break;
}
BN_bin2bn(der_bytes+4, der_bytes[3], sig->r);
BN_bin2bn(der_bytes+6+der_bytes[3], der_bytes[4+der_bytes[3]+1], sig->s);
pub = get_compressed_pub_for_key(key);
signature_wrapper.data = compact_r1(pub, sig, digest);
signature_wrapper.check = fc::crypto::checksummed_data<fc::crypto::r1::compact_signature>::calculate_checksum(signature_wrapper.data, "R1");
checksummed = fc::raw::pack(signature_wrapper);
cout << "signature(EOSR1" << fc::to_base58(checksummed.data(), checksummed.size()) << ")" << endl;
err:
CFRelease(key);
CFRelease(digestData);
}
void recover(vector<string> params) {
fc::sha256 digest(params[0]);
fc::crypto::signature sig(params[1]);
cout << fc::crypto::public_key(sig, digest) << endl;
}
int main(int argc, char* argv[]) {
pid_t pid = getpid();
SecCodeRef code = nullptr;
CFNumberRef pidnumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pid);
CFDictionaryRef piddict = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kSecGuestAttributePid, (const void**)&pidnumber, 1, nullptr, nullptr);
if(!SecCodeCopyGuestWithAttributes(nullptr, piddict, kSecCSDefaultFlags, &code)) {
if(SecCodeCheckValidity(code, kSecCSDefaultFlags, 0)) {
cout << "This application is NOT signed. For access to the secure enclave the application must be signed. Exiting." << endl;
return 1;
}
CFRelease(code);
}
CFRelease(piddict);
CFRelease(pidnumber);
po::options_description desc_help;
desc_help.add_options()("help", "halp");
po::options_description desc_create("Create and store a key; This application only allows a single key to be stored at a time");
desc_create.add_options()
("create-se", "Create a key to be stored in Secure Enclave; no TouchID requirements")
("create-se-interactive", "Create a key to be stored in Secure Enclave; fingerprint or password required on use")
("create-se-touch-only", "Create a key to be stored in Secure Enclave; fingerprint required on use");
po::options_description desc_key("Other key operations");
desc_key.add_options()
("print-attributes", "Print the attributes and public key of the stored key")
("remove", "Remove the key");
po::options_description sign_key("Signing & Key Recovery");
sign_key.add_options()
("sign", po::value<string>()->value_name("DIGEST"), "Sign sha256 via SE")
("recover", po::value<vector<string>>()->multitoken()->value_name("DIGEST COMPACT_SIG"), "Recover and print pubkey");
po::options_description all_desc;
all_desc.add(desc_create).add(desc_key).add(sign_key);
po::options_description all_desc_and_help;
all_desc_and_help.add(all_desc).add(desc_help);
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, all_desc_and_help), vm);
po::notify(vm);
if(vm.count("create-se"))
create(0);
else if(vm.count("create-se-touch-only"))
create(kSecAccessControlTouchIDAny);
else if(vm.count("create-se-interactive"))
create(kSecAccessControlUserPresence);
else if(vm.count("print-attributes"))
print_attributes();
else if(vm.count("remove"))
remove();
else if(vm.count("sign"))
sign(vm["sign"].as<string>());
else if (!vm["recover"].empty() && vm["recover"].as<vector<string>>().size() == 2)
recover(vm["recover"].as<vector<string>>());
else
cout << all_desc << endl;
return 0;
}
\ No newline at end of file
#include "r1_signature_compactor.hpp"
using namespace fc;
static int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
{
if (!eckey) FC_THROW_EXCEPTION( exception, "null key" );
int ret = 0;
BN_CTX *ctx = NULL;
BIGNUM *x = NULL;
BIGNUM *e = NULL;
BIGNUM *order = NULL;
BIGNUM *sor = NULL;
BIGNUM *eor = NULL;
BIGNUM *field = NULL;
EC_POINT *R = NULL;
EC_POINT *O = NULL;
EC_POINT *Q = NULL;
BIGNUM *rr = NULL;
BIGNUM *zero = NULL;
int n = 0;
int i = recid / 2;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
BN_CTX_start(ctx);
order = BN_CTX_get(ctx);
if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
x = BN_CTX_get(ctx);
if (!BN_copy(x, order)) { ret=-1; goto err; }
if (!BN_mul_word(x, i)) { ret=-1; goto err; }
if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
field = BN_CTX_get(ctx);
if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
if (check)
{
if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
}
if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
n = EC_GROUP_get_degree(group);
e = BN_CTX_get(ctx);
if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
zero = BN_CTX_get(ctx);
if (!BN_zero(zero)) { ret=-1; goto err; }
if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
rr = BN_CTX_get(ctx);
if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
sor = BN_CTX_get(ctx);
if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
eor = BN_CTX_get(ctx);
if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
ret = 1;
err:
if (ctx) {
BN_CTX_end(ctx);
BN_CTX_free(ctx);
}
if (R != NULL) EC_POINT_free(R);
if (O != NULL) EC_POINT_free(O);
if (Q != NULL) EC_POINT_free(Q);
return ret;
}
fc::crypto::r1::compact_signature compact_r1(fc::crypto::r1::public_key_data& pubkey, fc::ecdsa_sig& sig, fc::sha256& digest) {
fc::crypto::r1::compact_signature csig;
int nBitsR = BN_num_bits(sig->r);
int nBitsS = BN_num_bits(sig->s);
if (nBitsR <= 256 && nBitsS <= 256)
{
int nRecId = -1;
for (int i=0; i<4; i++)
{
////public_key keyRec;
EC_KEY* key = EC_KEY_new_by_curve_name( NID_X9_62_prime256v1 );
if (ECDSA_SIG_recover_key_GFp(key, sig, (unsigned char*)digest.data(), digest.data_size(), i, 1) == 1)
{
EC_KEY_set_conv_form(key, POINT_CONVERSION_COMPRESSED );
unsigned char* pubcheck = nullptr;
int s = i2o_ECPublicKey(key, &pubcheck);
if (memcmp(pubcheck, pubkey.data, pubkey.size()) == 0)
{
nRecId = i;
free(pubcheck);
break;
}
if(s > 0)
free(pubcheck);
}
}
if (nRecId == -1)
{
FC_THROW_EXCEPTION( exception, "unable to construct recoverable key");
}
unsigned char* result = nullptr;
auto bytes = i2d_ECDSA_SIG( sig, &result );
auto lenR = result[3];
auto lenS = result[5+lenR];
auto idxR = 4;
auto idxS = 6+lenR;
if(result[idxR] == 0x00) {
++idxR;
--lenR;
}
if(result[idxS] == 0x00) {
++idxS;
--lenS;
}
memcpy( &csig.data[1], &result[idxR], lenR );
memcpy( &csig.data[33], &result[idxS], lenS );
free(result);
csig.data[0] = nRecId+27+4;
}
return csig;
}
\ No newline at end of file
#pragma once
#include <fc/crypto/elliptic_r1.hpp>
#include <fc/crypto/openssl.hpp>
#include <fc/crypto/sha256.hpp>
fc::crypto::r1::compact_signature compact_r1(fc::crypto::r1::public_key_data& pubkey, fc::ecdsa_sig& sig, fc::sha256& digest);
\ No newline at end of file
#!/bin/bash
set -eu
if [ $# -ne 3 ]; then
echo Usage: $0 certificate-fingerprint appid provisioning-file
exit 1
fi
CERT=$1
APPID=$2
PP=$3
IFS=. read TEAMID BUNDLEID <<<"${APPID}"
mkdir -p applesedemo.app/Contents/MacOS
cp "$3" applesedemo.app/Contents/embedded.provisionprofile
cp applesedemo applesedemo.app/Contents/MacOS/applesedemo
cat > applesedemo.app/Contents/Info.plist <<ENDENDEND
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>applesedemo</string>
<key>CFBundleIdentifier</key>
<string>$BUNDLEID</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>applesedemo</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
ENDENDEND
TMPFILE=$(mktemp /tmp/signscript.XXXXXX)
trap "rm $TMPFILE" EXIT
cat > $TMPFILE <<ENDENDEND
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>$APPID</string>
<key>com.apple.developer.team-identifier</key>
<string>$TEAMID</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
</dict>
</plist>
ENDENDEND
codesign --force --sign $CERT --timestamp=none --entitlements $TMPFILE applesedemo.app
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册