feat: v1.10.12, se-recover, se-ecdh support public key point
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -487,7 +487,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.10.11"
|
version = "1.10.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"authenticator 0.3.1",
|
"authenticator 0.3.1",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "card-cli"
|
name = "card-cli"
|
||||||
version = "1.10.11"
|
version = "1.10.12"
|
||||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use crate::keyutil::{parse_key_uri, KeyUri};
|
use crate::keyutil::{parse_key_uri, KeyUri};
|
||||||
use crate::seutil;
|
use crate::seutil;
|
||||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
|
use p256::elliptic_curve::sec1::FromEncodedPoint;
|
||||||
|
use p256::{EncodedPoint, PublicKey};
|
||||||
use rust_util::util_clap::{Command, CommandError};
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
use rust_util::util_msg;
|
use rust_util::util_msg;
|
||||||
|
use spki::EncodePublicKey;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub struct CommandImpl;
|
pub struct CommandImpl;
|
||||||
@@ -47,9 +50,31 @@ impl Command for CommandImpl {
|
|||||||
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
|
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
|
||||||
debugging!("Secure enclave key URI: {:?}", se_key_uri);
|
debugging!("Secure enclave key URI: {:?}", se_key_uri);
|
||||||
|
|
||||||
let ephemeral_public_key_bytes = hex::decode(epk)?;
|
let ephemeral_public_key_der_bytes;
|
||||||
let dh =
|
if epk.starts_with("04") {
|
||||||
seutil::secure_enclave_p256_dh(&se_key_uri.private_key, &ephemeral_public_key_bytes)?;
|
let ephemeral_public_key_point_bytes = opt_result!(
|
||||||
|
hex::decode(epk),
|
||||||
|
"Decode public key point from hex failed: {}"
|
||||||
|
);
|
||||||
|
let encoded_point = opt_result!(
|
||||||
|
EncodedPoint::from_bytes(ephemeral_public_key_point_bytes),
|
||||||
|
"Parse public key point failed: {}"
|
||||||
|
);
|
||||||
|
let public_key_opt = PublicKey::from_encoded_point(&encoded_point);
|
||||||
|
if public_key_opt.is_none().into() {
|
||||||
|
return simple_error!("Parse public key failed.");
|
||||||
|
}
|
||||||
|
let public_key = public_key_opt.unwrap();
|
||||||
|
ephemeral_public_key_der_bytes = public_key.to_public_key_der()?.as_bytes().to_vec();
|
||||||
|
} else {
|
||||||
|
ephemeral_public_key_der_bytes =
|
||||||
|
opt_result!(hex::decode(epk), "Decode public key from hex failed: {}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let dh = seutil::secure_enclave_p256_dh(
|
||||||
|
&se_key_uri.private_key,
|
||||||
|
&ephemeral_public_key_der_bytes,
|
||||||
|
)?;
|
||||||
let dh_hex = hex::encode(&dh);
|
let dh_hex = hex::encode(&dh);
|
||||||
|
|
||||||
if json_output {
|
if json_output {
|
||||||
|
|||||||
67
src/cmd_se_recover.rs
Normal file
67
src/cmd_se_recover.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage};
|
||||||
|
use crate::pkiutil::bytes_to_pem;
|
||||||
|
use crate::seutil;
|
||||||
|
use crate::util::base64_encode;
|
||||||
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||||
|
use rust_util::util_clap::{Command, CommandError};
|
||||||
|
use rust_util::util_msg;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
pub struct CommandImpl;
|
||||||
|
|
||||||
|
impl Command for CommandImpl {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"se-recover"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subcommand<'a>(&self) -> App<'a, 'a> {
|
||||||
|
SubCommand::with_name(self.name())
|
||||||
|
.about("Secure Enclave recover subcommand")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("key")
|
||||||
|
.long("key")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Key uri"),
|
||||||
|
)
|
||||||
|
.arg(Arg::with_name("json").long("json").help("JSON output"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
|
||||||
|
if !seutil::is_support_se() {
|
||||||
|
return simple_error!("Secure Enclave is NOT supported.");
|
||||||
|
}
|
||||||
|
let key = sub_arg_matches.value_of("key").unwrap();
|
||||||
|
|
||||||
|
let json_output = sub_arg_matches.is_present("json");
|
||||||
|
if json_output {
|
||||||
|
util_msg::set_logger_std_out(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
|
||||||
|
debugging!("Secure enclave key URI: {:?}", se_key_uri);
|
||||||
|
|
||||||
|
let (public_key_point, public_key_der, _private_key) =
|
||||||
|
seutil::recover_secure_enclave_p256_public_key(
|
||||||
|
&se_key_uri.private_key,
|
||||||
|
se_key_uri.usage == KeyUsage::Singing,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let public_key_point_hex = hex::encode(&public_key_point);
|
||||||
|
let public_key_pem = bytes_to_pem("PUBLIC KEY", &*public_key_der);
|
||||||
|
if json_output {
|
||||||
|
let mut json = BTreeMap::<&'_ str, String>::new();
|
||||||
|
json.insert("public_key_point", public_key_point_hex);
|
||||||
|
json.insert("public_key_pem", base64_encode(&*public_key_der));
|
||||||
|
json.insert("key", key.to_string());
|
||||||
|
|
||||||
|
println!("{}", serde_json::to_string_pretty(&json).unwrap());
|
||||||
|
} else {
|
||||||
|
success!("Public key(point): {}", public_key_point_hex);
|
||||||
|
success!("Public key PEM: \n{}", public_key_pem);
|
||||||
|
success!("Key: {}", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,8 @@ mod cmd_se_ecdh;
|
|||||||
mod cmd_se_ecsign;
|
mod cmd_se_ecsign;
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
mod cmd_se_generate;
|
mod cmd_se_generate;
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
mod cmd_se_recover;
|
||||||
mod cmd_signfile;
|
mod cmd_signfile;
|
||||||
mod cmd_signjwt;
|
mod cmd_signjwt;
|
||||||
mod cmd_sshagent;
|
mod cmd_sshagent;
|
||||||
@@ -134,6 +136,8 @@ fn inner_main() -> CommandError {
|
|||||||
#[cfg(feature = "with-secure-enclave")]
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
Box::new(cmd_se_generate::CommandImpl),
|
Box::new(cmd_se_generate::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
|
Box::new(cmd_se_recover::CommandImpl),
|
||||||
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
Box::new(cmd_se_ecsign::CommandImpl),
|
Box::new(cmd_se_ecsign::CommandImpl),
|
||||||
#[cfg(feature = "with-secure-enclave")]
|
#[cfg(feature = "with-secure-enclave")]
|
||||||
Box::new(cmd_se_ecdh::CommandImpl),
|
Box::new(cmd_se_ecdh::CommandImpl),
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ swift!(fn generate_secure_enclave_p256_ecdh_keypair() -> SRString);
|
|||||||
swift!(fn generate_secure_enclave_p256_ecsign_keypair() -> SRString);
|
swift!(fn generate_secure_enclave_p256_ecsign_keypair() -> SRString);
|
||||||
swift!(fn compute_secure_enclave_p256_ecdh(private_key_base64: SRString, ephemera_public_key_base64: SRString) -> SRString);
|
swift!(fn compute_secure_enclave_p256_ecdh(private_key_base64: SRString, ephemera_public_key_base64: SRString) -> SRString);
|
||||||
swift!(fn compute_secure_enclave_p256_ecsign(private_key_base64: SRString, content: SRString) -> SRString);
|
swift!(fn compute_secure_enclave_p256_ecsign(private_key_base64: SRString, content: SRString) -> SRString);
|
||||||
|
swift!(fn recover_secure_enclave_p256_ecsign_public_key(private_key_base64: SRString) -> SRString);
|
||||||
|
swift!(fn recover_secure_enclave_p256_ecdh_public_key(private_key_base64: SRString) -> SRString);
|
||||||
|
|
||||||
pub fn is_support_se() -> bool {
|
pub fn is_support_se() -> bool {
|
||||||
unsafe { is_support_secure_enclave() }
|
unsafe { is_support_secure_enclave() }
|
||||||
@@ -19,31 +21,19 @@ pub fn generate_secure_enclave_p256_keypair(sign: bool) -> XResult<(Vec<u8>, Vec
|
|||||||
} else {
|
} else {
|
||||||
unsafe { generate_secure_enclave_p256_ecdh_keypair() }
|
unsafe { generate_secure_enclave_p256_ecdh_keypair() }
|
||||||
};
|
};
|
||||||
let p256_keypair_result_str = p256_keypair_result.as_str();
|
parse_p256_keypair_result(p256_keypair_result.as_str())
|
||||||
if !p256_keypair_result_str.starts_with("ok:") {
|
}
|
||||||
return simple_error!(
|
|
||||||
"Generate P256 in secure enclave failed: {}",
|
pub fn recover_secure_enclave_p256_public_key(
|
||||||
p256_keypair_result_str
|
private_key: &str,
|
||||||
);
|
sign: bool,
|
||||||
}
|
) -> XResult<(Vec<u8>, Vec<u8>, String)> {
|
||||||
let public_key_and_private_key = p256_keypair_result_str.chars().skip(3).collect::<String>();
|
let p256_keypair_result = if sign {
|
||||||
let public_key_and_private_keys = public_key_and_private_key.split(',').collect::<Vec<_>>();
|
unsafe { recover_secure_enclave_p256_ecsign_public_key(SRString::from(private_key)) }
|
||||||
if public_key_and_private_keys.len() != 3 {
|
} else {
|
||||||
return simple_error!(
|
unsafe { recover_secure_enclave_p256_ecdh_public_key(SRString::from(private_key)) }
|
||||||
"Generate P256 in secure enclave result is bad: {}",
|
};
|
||||||
public_key_and_private_key
|
parse_p256_keypair_result(p256_keypair_result.as_str())
|
||||||
);
|
|
||||||
}
|
|
||||||
let public_key_point = opt_result!(
|
|
||||||
base64_decode(public_key_and_private_keys[0]),
|
|
||||||
"Public key point is not base64 encoded: {}"
|
|
||||||
);
|
|
||||||
let public_key_der = opt_result!(
|
|
||||||
base64_decode(public_key_and_private_keys[1]),
|
|
||||||
"Public key der is not base64 encoded: {}"
|
|
||||||
);
|
|
||||||
let private_key = public_key_and_private_keys[2].to_string();
|
|
||||||
Ok((public_key_point, public_key_der, private_key))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn secure_enclave_p256_dh(
|
pub fn secure_enclave_p256_dh(
|
||||||
@@ -94,3 +84,30 @@ pub fn secure_enclave_p256_sign(private_key: &str, content: &[u8]) -> XResult<Ve
|
|||||||
debugging!("Signature: {}", &signature);
|
debugging!("Signature: {}", &signature);
|
||||||
Ok(base64_decode(&signature)?)
|
Ok(base64_decode(&signature)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_p256_keypair_result(p256_keypair_result_str: &str) -> XResult<(Vec<u8>, Vec<u8>, String)> {
|
||||||
|
if !p256_keypair_result_str.starts_with("ok:") {
|
||||||
|
return simple_error!(
|
||||||
|
"Generate P256 in secure enclave failed: {}",
|
||||||
|
p256_keypair_result_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let public_key_and_private_key = p256_keypair_result_str.chars().skip(3).collect::<String>();
|
||||||
|
let public_key_and_private_keys = public_key_and_private_key.split(',').collect::<Vec<_>>();
|
||||||
|
if public_key_and_private_keys.len() != 3 {
|
||||||
|
return simple_error!(
|
||||||
|
"Generate P256 in secure enclave result is bad: {}",
|
||||||
|
public_key_and_private_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let public_key_point = opt_result!(
|
||||||
|
base64_decode(public_key_and_private_keys[0]),
|
||||||
|
"Public key point is not base64 encoded: {}"
|
||||||
|
);
|
||||||
|
let public_key_der = opt_result!(
|
||||||
|
base64_decode(public_key_and_private_keys[1]),
|
||||||
|
"Public key der is not base64 encoded: {}"
|
||||||
|
);
|
||||||
|
let private_key = public_key_and_private_keys[2].to_string();
|
||||||
|
Ok((public_key_point, public_key_der, private_key))
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,6 +53,48 @@ func generateSecureEnclaveP256KeyPair(sign: Bool) -> SRString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@_cdecl("recover_secure_enclave_p256_ecsign_public_key")
|
||||||
|
func recoverSecureEnclaveP256PublicKeyEcsign(privateKeyDataRepresentation: SRString) -> SRString {
|
||||||
|
return recoverSecureEnclaveP256PublicKey(privateKeyDataRepresentation: privateKeyDataRepresentation, sign: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@_cdecl("recover_secure_enclave_p256_ecdh_public_key")
|
||||||
|
func recoverSecureEnclaveP256PublicKeyEcdh(privateKeyDataRepresentation: SRString) -> SRString {
|
||||||
|
return recoverSecureEnclaveP256PublicKey(privateKeyDataRepresentation: privateKeyDataRepresentation, sign: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
func recoverSecureEnclaveP256PublicKey(privateKeyDataRepresentation: SRString, sign: Bool) -> SRString {
|
||||||
|
guard let privateKeyDataRepresentation = Data(
|
||||||
|
base64Encoded: privateKeyDataRepresentation.toString()
|
||||||
|
) else {
|
||||||
|
return SRString("err:private key base64 decode failed")
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let context = LAContext();
|
||||||
|
if (sign) {
|
||||||
|
let privateKeyReference = try SecureEnclave.P256.Signing.PrivateKey(
|
||||||
|
dataRepresentation: privateKeyDataRepresentation,
|
||||||
|
authenticationContext: context
|
||||||
|
)
|
||||||
|
let publicKeyBase64 = privateKeyReference.publicKey.x963Representation.base64EncodedString()
|
||||||
|
let publicKeyPem = privateKeyReference.publicKey.derRepresentation.base64EncodedString()
|
||||||
|
let dataRepresentationBase64 = privateKeyReference.dataRepresentation.base64EncodedString()
|
||||||
|
return SRString("ok:\(publicKeyBase64),\(publicKeyPem),\(dataRepresentationBase64)")
|
||||||
|
} else {
|
||||||
|
let privateKeyReference = try SecureEnclave.P256.KeyAgreement.PrivateKey(
|
||||||
|
dataRepresentation: privateKeyDataRepresentation,
|
||||||
|
authenticationContext: context
|
||||||
|
)
|
||||||
|
let publicKeyBase64 = privateKeyReference.publicKey.x963Representation.base64EncodedString()
|
||||||
|
let publicKeyPem = privateKeyReference.publicKey.derRepresentation.base64EncodedString()
|
||||||
|
let dataRepresentationBase64 = privateKeyReference.dataRepresentation.base64EncodedString()
|
||||||
|
return SRString("ok:\(publicKeyBase64),\(publicKeyPem),\(dataRepresentationBase64)")
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return SRString("err:\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@_cdecl("compute_secure_enclave_p256_ecdh")
|
@_cdecl("compute_secure_enclave_p256_ecdh")
|
||||||
func computeSecureEnclaveP256Ecdh(privateKeyDataRepresentation: SRString, ephemeraPublicKey: SRString) -> SRString {
|
func computeSecureEnclaveP256Ecdh(privateKeyDataRepresentation: SRString, ephemeraPublicKey: SRString) -> SRString {
|
||||||
guard let privateKeyDataRepresentation = Data(
|
guard let privateKeyDataRepresentation = Data(
|
||||||
|
|||||||
Reference in New Issue
Block a user