-
Notifications
You must be signed in to change notification settings - Fork 14
Open
Description
Can't find symbols to generate ed25519 keypair
import ctypes
from objc_util import *
import base64
import struct
import uuid
# --- Constants for Security.framework ---
c = ctypes.CDLL(None)
kSecAttrKeyType = c_void_p.in_dll(c, 'kSecAttrKeyType')
kSecAttrKeyTypeRSA = c_void_p.in_dll(c, 'kSecAttrKeyTypeRSA')
kSecAttrKeyTypeEC = c_void_p.in_dll(c, 'kSecAttrKeyTypeEC')
# NEW: The correct constant for Ed25519 is kSecAttrKeyTypeCurve25519
kSecAttrKeyTypeCurve25519 = c_void_p.in_dll(c, 'kSecAttrKeyTypeCurve25519')
kSecAttrKeySizeInBits = c_void_p.in_dll(c, 'kSecAttrKeySizeInBits')
kSecPrivateKeyAttrs = c_void_p.in_dll(c, 'kSecPrivateKeyAttrs')
kSecPublicKeyAttrs = c_void_p.in_dll(c, 'kSecPublicKeyAttrs')
kSecAttrIsPermanent = c_void_p.in_dll(c, 'kSecAttrIsPermanent')
kCFBooleanTrue = c_void_p.in_dll(c, 'kCFBooleanTrue')
kCFBooleanFalse = c_void_p.in_dll(c, 'kCFBooleanFalse')
kSecAttrIsExtractable = c_void_p.in_dll(c, 'kSecAttrIsExtractable')
kSecAttrApplicationTag = c_void_p.in_dll(c, 'kSecAttrApplicationTag')
# --- Native C function declarations (unchanged) ---
SecKeyGeneratePair = c.SecKeyGeneratePair
SecKeyGeneratePair.restype = c_void_p
SecKeyGeneratePair.argtypes = [c_void_p, c_void_p, c_void_p]
SecKeyCopyExternalRepresentation = c.SecKeyCopyExternalRepresentation
SecKeyCopyExternalRepresentation.restype = c_void_p
SecKeyCopyExternalRepresentation.argtypes = [c_void_p, c_void_p]
CFRelease = c.CFRelease
CFRelease.restype = None
CFRelease.argtypes = [c_void_p]
CFDataGetLength = c.CFDataGetLength
CFDataGetLength.restype = ctypes.c_long
CFDataGetLength.argtypes = [c_void_p]
CFDataGetBytePtr = c.CFDataGetBytePtr
CFDataGetBytePtr.restype = ctypes.POINTER(ctypes.c_byte)
CFDataGetBytePtr.argtypes = [c_void_p]
SecItemCopyMatching = c.SecItemCopyMatching
SecItemCopyMatching.restype = ctypes.c_int32
SecItemCopyMatching.argtypes = [c_void_p, c_void_p]
kSecClass = c_void_p.in_dll(c, 'kSecClass')
kSecClassKey = c_void_p.in_dll(c, 'kSecClassKey')
kSecReturnRef = c_void_p.in_dll(c, 'kSecReturnRef')
# --- Helper functions (unchanged) ---
def encode_string(s):
if not isinstance(s, bytes):
s = s.encode('utf-8')
return struct.pack('>I', len(s)) + s
# This parser is specific to RSA and not used for EC or Ed25519
def parse_der_asn1_rsa(der_data):
if not isinstance(der_data, bytes):
raise TypeError(f"Expected bytes, but received {type(der_data)}.")
if not der_data.startswith(b'\x30'):
raise ValueError("Invalid ASN.1 data: Does not start with SEQUENCE.")
modulus_start_index = der_data.find(b'\x02')
if modulus_start_index == -1:
raise ValueError("Modulus INTEGER not found.")
exponent_start_index = der_data.find(b'\x02', modulus_start_index + 1)
if exponent_start_index == -1:
raise ValueError("Exponent INTEGER not found.")
modulus_length_bytes = der_data[modulus_start_index+1]
if modulus_length_bytes & 0x80:
length_bytes_count = modulus_length_bytes & 0x7F
modulus_length = int.from_bytes(der_data[modulus_start_index+2:modulus_start_index+2+length_bytes_count], 'big')
modulus_data_start = modulus_start_index + 2 + length_bytes_count
else:
modulus_length = modulus_length_bytes
modulus_data_start = modulus_start_index + 2
modulus = der_data[modulus_data_start:modulus_data_start + modulus_length]
exponent_length_bytes = der_data[exponent_start_index+1]
if exponent_length_bytes & 0x80:
length_bytes_count = exponent_length_bytes & 0x7F
exponent_length = int.from_bytes(der_data[exponent_start_index+2:exponent_start_index+2+length_bytes_count], 'big')
exponent_data_start = exponent_start_index + 2 + length_bytes_count
else:
exponent_length = exponent_length_bytes
exponent_data_start = exponent_start_index + 2
exponent = der_data[exponent_data_start:exponent_data_start + exponent_length]
return exponent, modulus
def generate_key_pair(key_type, key_size_bits, application_tag=None, exportable=False):
"""
Generates a new key pair of a specified type.
Args:
key_type: A C-void_p constant for the key type (e.g., kSecAttrKeyTypeEC, kSecAttrKeyTypeRSA).
key_size_bits: The desired key size in bits.
application_tag: A unique tag to store the private key in the Keychain.
exportable: If True, the private key can be extracted and exported.
Returns:
A tuple of (public_key, private_key) ObjCInstances.
"""
public_attrs = NSMutableDictionary.alloc().init()
public_attrs.setObject_forKey_(kCFBooleanFalse, kSecAttrIsPermanent)
private_attrs = NSMutableDictionary.alloc().init()
if application_tag:
private_attrs.setObject_forKey_(kCFBooleanTrue, kSecAttrIsPermanent)
private_attrs.setObject_forKey_(ns(application_tag), kSecAttrApplicationTag)
else:
private_attrs.setObject_forKey_(kCFBooleanFalse, kSecAttrIsPermanent)
if exportable:
private_attrs.setObject_forKey_(kCFBooleanTrue, kSecAttrIsExtractable)
else:
private_attrs.setObject_forKey_(kCFBooleanFalse, kSecAttrIsExtractable)
key_gen_attrs = NSMutableDictionary.alloc().init()
key_gen_attrs.setObject_forKey_(key_type, kSecAttrKeyType)
key_gen_attrs.setObject_forKey_(NSNumber.numberWithInt_(key_size_bits), kSecAttrKeySizeInBits)
key_gen_attrs.setObject_forKey_(public_attrs, kSecPublicKeyAttrs)
key_gen_attrs.setObject_forKey_(private_attrs, kSecPrivateKeyAttrs)
public_key_ptr = c_void_p(0)
private_key_ptr = c_void_p(0)
error_ptr = SecKeyGeneratePair(key_gen_attrs, byref(public_key_ptr), byref(private_key_ptr))
if error_ptr:
CFRelease(error_ptr)
print(f"Error generating key pair: {error_ptr}")
return None, None
else:
public_key = ObjCInstance(public_key_ptr)
private_key = ObjCInstance(private_key_ptr)
return public_key, private_key
def find_private_key(application_tag):
"""
Finds a private key in the Keychain using its application tag.
"""
query = NSMutableDictionary.alloc().init()
query.setObject_forKey_(kSecClassKey, kSecClass)
query.setObject_forKey_(ns(application_tag), kSecAttrApplicationTag)
query.setObject_forKey_(kCFBooleanTrue, kSecReturnRef)
key_ref_ptr = c_void_p(0)
status = SecItemCopyMatching(query, byref(key_ref_ptr))
if status == 0 and key_ref_ptr.value:
return ObjCInstance(key_ref_ptr)
else:
print(f"Key not found or error occurred. Status code: {status}")
if key_ref_ptr.value:
CFRelease(key_ref_ptr)
return None
def to_pem_format(key_data, key_type="PUBLIC KEY"):
"""
Converts raw key data to a PEM-formatted string.
"""
header = f"-----BEGIN {key_type}-----\n"
footer = f"\n-----END {key_type}-----"
encoded_data = base64.b64encode(key_data).decode('utf-8')
pem_string = header + '\n'.join(encoded_data[i:i+64] for i in range(0, len(encoded_data), 64)) + footer
return pem_string
# --- Main execution block for Ed25519 ---
if __name__ == "__main__":
key_size = 256 # The key size parameter is a formality for Curve25519
key_tag = f"com.example.curve25519key.{uuid.uuid4()}"
private_key_file_path = "curve25519_private_key.pem"
public_key_file_path = "curve25519_public_key.pem"
print(f"Attempting to generate an exportable {key_size}-bit Ed25519 key pair with tag: {key_tag}")
pub_key, priv_key = generate_key_pair(kSecAttrKeyTypeCurve25519, key_size, application_tag=key_tag, exportable=True)
if pub_key and priv_key:
print("Ed25519 key pair generated successfully!")
# --- Export and save the private key ---
print("\nExporting the private key data...")
priv_key_data_ref = SecKeyCopyExternalRepresentation(priv_key.ptr, None)
if priv_key_data_ref:
try:
priv_data_ptr = CFDataGetBytePtr(priv_key_data_ref)
priv_data_len = CFDataGetLength(priv_key_data_ref)
priv_bytes = ctypes.string_at(priv_data_ptr, priv_data_len)
CFRelease(priv_key_data_ref)
# The PEM type for Ed25519 keys is commonly "PRIVATE KEY"
pem_key = to_pem_format(priv_bytes, "PRIVATE KEY")
print("\nPrivate Key (PEM format):")
print(pem_key)
with open(private_key_file_path, "w") as f:
f.write(pem_key)
print(f"\nPrivate key successfully saved to '{private_key_file_path}'.")
except (ValueError, TypeError) as e:
print(f"Error exporting private key data: {e}")
else:
print("Error: Could not get the external representation of the private key.")
# --- Export and save the public key ---
print("\nExporting the public key data...")
pub_key_data_ref = SecKeyCopyExternalRepresentation(pub_key.ptr, None)
if pub_key_data_ref:
try:
pub_data_ptr = CFDataGetBytePtr(pub_key_data_ref)
pub_data_len = CFDataGetLength(pub_key_data_ref)
pub_bytes = ctypes.string_at(pub_data_ptr, pub_data_len)
CFRelease(pub_key_data_ref)
# The public key PEM type is "PUBLIC KEY"
pem_pub_key = to_pem_format(pub_bytes, "PUBLIC KEY")
print("\nPublic Key (PEM format):")
print(pem_pub_key)
with open(public_key_file_path, "w") as f:
f.write(pem_pub_key)
print(f"\nPublic key successfully saved to '{public_key_file_path}'.")
except (ValueError, TypeError) as e:
print(f"Error exporting public key data: {e}")
else:
print("Error: Could not get the external representation of the public key.")
else:
print("Error: Failed to generate the Ed25519 key pair.")
Metadata
Metadata
Assignees
Labels
No labels