Skip to content

Commit 2ffedac

Browse files
authored
Merge pull request #341 from THS-on/public-to-subjectpublickeyinfo
Add support for converting Public to SubjectPublicKeyInfo
2 parents d5021d1 + 1e3277c commit 2ffedac

File tree

5 files changed

+331
-0
lines changed

5 files changed

+331
-0
lines changed

tss-esapi/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ hostname-validator = "1.1.0"
2323
regex = "1.3.9"
2424
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
2525
tss-esapi-sys = { path = "../tss-esapi-sys", version = "0.3.0" }
26+
oid = "0.2.1"
27+
picky-asn1 = "0.3.0"
28+
picky-asn1-x509 = "0.6.1"
2629

2730
[dev-dependencies]
2831
env_logger = "0.9.0"

tss-esapi/src/abstraction/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod cipher;
66
pub mod ek;
77
pub mod nv;
88
pub mod pcr;
9+
pub mod public;
910
pub mod transient;
1011

1112
use crate::{attributes::ObjectAttributesBuilder, structures::PublicBuilder};

tss-esapi/src/abstraction/public.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2022 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::interface_types::ecc::EccCurve;
5+
use crate::structures::{Public, RsaExponent};
6+
use crate::{Error, WrapperErrorKind};
7+
8+
use core::convert::TryFrom;
9+
use oid::ObjectIdentifier;
10+
use picky_asn1::bit_string::BitString;
11+
use picky_asn1::wrapper::{IntegerAsn1, OctetStringAsn1};
12+
use picky_asn1_x509::{
13+
AlgorithmIdentifier, EcParameters, EcPoint, PublicKey, RsaPublicKey, SubjectPublicKeyInfo,
14+
};
15+
use serde::{Deserialize, Serialize};
16+
17+
/// Can be converted from [`crate::structures::Public`] when not a fully constructed
18+
/// [`picky_asn1_x509::SubjectPublicKeyInfo`] is required.
19+
///
20+
/// # Details
21+
///
22+
/// Holds either [`picky_asn1_x509::RsaPublicKey`] for [`crate::structures::Public::Rsa`] or
23+
/// [`picky_asn1_x509::EcPoint`] for [`crate::structures::Public::Ecc`].
24+
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
25+
pub enum DecodedKey {
26+
RsaPublicKey(RsaPublicKey),
27+
EcPoint(EcPoint),
28+
}
29+
30+
impl TryFrom<Public> for DecodedKey {
31+
type Error = Error;
32+
33+
fn try_from(value: Public) -> Result<Self, Self::Error> {
34+
public_to_decoded_key(&value)
35+
}
36+
}
37+
38+
impl TryFrom<Public> for SubjectPublicKeyInfo {
39+
type Error = Error;
40+
41+
/// Converts [`crate::structures::Public::Rsa`] and [`crate::structures::Public::Ecc`] to [`picky_asn1_x509::SubjectPublicKeyInfo`].
42+
///
43+
/// # Details
44+
/// The result can be used to convert TPM public keys to DER using `picky_asn1_der`.
45+
///
46+
/// # Errors
47+
/// * if other instances of [`crate::structures::Public`] are used `UnsupportedParam` will be returned.
48+
fn try_from(value: Public) -> Result<Self, Self::Error> {
49+
let decoded_key = public_to_decoded_key(&value)?;
50+
51+
match (value, decoded_key) {
52+
(Public::Rsa { .. }, DecodedKey::RsaPublicKey(key)) => Ok(SubjectPublicKeyInfo {
53+
algorithm: AlgorithmIdentifier::new_rsa_encryption(),
54+
subject_public_key: PublicKey::Rsa(key.into()),
55+
}),
56+
(Public::Ecc { parameters, .. }, DecodedKey::EcPoint(point)) => {
57+
Ok(SubjectPublicKeyInfo {
58+
algorithm: AlgorithmIdentifier::new_elliptic_curve(EcParameters::NamedCurve(
59+
curve_oid(parameters.ecc_curve())?.into(),
60+
)),
61+
subject_public_key: PublicKey::Ec(BitString::with_bytes(point).into()),
62+
})
63+
}
64+
_ => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
65+
}
66+
}
67+
}
68+
69+
/// Converts [`crate::structures::Public::Rsa`] and [`crate::structures::Public::Ecc`] to [DecodedKey].
70+
///
71+
/// # Details
72+
/// Does basic key conversion to either RSA or ECC. In RSA conversion the TPM zero exponent is replaced with `65537`.
73+
///
74+
/// # Errors
75+
/// * if other instances of [`crate::structures::Public`] are used `UnsupportedParam` will be returned.
76+
fn public_to_decoded_key(public: &Public) -> Result<DecodedKey, Error> {
77+
match public {
78+
Public::Rsa {
79+
unique, parameters, ..
80+
} => {
81+
let exponent = match parameters.exponent() {
82+
RsaExponent::ZERO_EXPONENT => 65537,
83+
_ => parameters.exponent().value(),
84+
}
85+
.to_be_bytes();
86+
Ok(DecodedKey::RsaPublicKey(RsaPublicKey {
87+
modulus: IntegerAsn1::from_bytes_be_unsigned(unique.value().to_vec()),
88+
public_exponent: IntegerAsn1::from_bytes_be_signed(exponent.to_vec()),
89+
}))
90+
}
91+
Public::Ecc { unique, .. } => {
92+
let x = unique.x().value().to_vec();
93+
let y = unique.y().value().to_vec();
94+
Ok(DecodedKey::EcPoint(OctetStringAsn1(
95+
elliptic_curve_point_to_octet_string(x, y),
96+
)))
97+
}
98+
99+
_ => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
100+
}
101+
}
102+
103+
// Taken from https://github.com/parallaxsecond/parsec/blob/561235f3cc37bcff3d9a6cb29c84eeae5d55100b/src/providers/tpm/utils.rs#L319
104+
// Points on elliptic curves are represented as defined in section 2.3.3 of https://www.secg.org/sec1-v2.pdf
105+
// The (uncompressed) representation is [ 0x04 || x || y ] where x and y are the coordinates of the point
106+
fn elliptic_curve_point_to_octet_string(mut x: Vec<u8>, mut y: Vec<u8>) -> Vec<u8> {
107+
let mut octet_string = vec![0x04];
108+
octet_string.append(&mut x);
109+
octet_string.append(&mut y);
110+
octet_string
111+
}
112+
113+
// Map TPM supported ECC curves to their respective OIDs
114+
fn curve_oid(ecc_curve: EccCurve) -> Result<ObjectIdentifier, Error> {
115+
match ecc_curve {
116+
EccCurve::NistP192 => Ok(picky_asn1_x509::oids::secp192r1()),
117+
EccCurve::NistP224 => Ok(picky_asn1_x509::oids::secp256r1()),
118+
EccCurve::NistP256 => Ok(picky_asn1_x509::oids::secp256r1()),
119+
EccCurve::NistP384 => Ok(picky_asn1_x509::oids::secp384r1()),
120+
EccCurve::NistP521 => Ok(picky_asn1_x509::oids::secp521r1()),
121+
// Barreto-Naehrig curves seem to not have any OIDs
122+
EccCurve::BnP256 => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
123+
EccCurve::BnP638 => Err(Error::local_error(WrapperErrorKind::UnsupportedParam)),
124+
EccCurve::Sm2P256 => Ok(ObjectIdentifier::try_from("1.2.156.10197.1.301").unwrap()),
125+
}
126+
}

tss-esapi/tests/integration_tests/abstraction_tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ mod ek_tests;
55
mod nv_tests;
66
mod pcr_data_tests;
77
mod pcr_tests;
8+
mod public_tests;
89
mod transient_key_context_tests;
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright 2022 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
mod public_rsa_test {
5+
use picky_asn1::wrapper::IntegerAsn1;
6+
use picky_asn1_x509::{AlgorithmIdentifier, PublicKey, SubjectPublicKeyInfo};
7+
use std::convert::TryFrom;
8+
use tss_esapi::{
9+
abstraction::public::DecodedKey,
10+
attributes::ObjectAttributesBuilder,
11+
interface_types::{
12+
algorithm::{HashingAlgorithm, PublicAlgorithm, RsaSchemeAlgorithm},
13+
key_bits::RsaKeyBits,
14+
},
15+
structures::{Public, PublicBuilder, PublicKeyRsa, PublicRsaParametersBuilder, RsaScheme},
16+
};
17+
18+
const RSA_KEY: [u8; 256] = [
19+
0xc9, 0x75, 0xf8, 0xb2, 0x30, 0xf4, 0x24, 0x6e, 0x95, 0xb1, 0x3c, 0x55, 0x0f, 0xe4, 0x48,
20+
0xe9, 0xac, 0x06, 0x1f, 0xa8, 0xbe, 0xa4, 0xd7, 0x1c, 0xa5, 0x5e, 0x2a, 0xbf, 0x60, 0xc2,
21+
0x98, 0x63, 0x6c, 0xb4, 0xe2, 0x61, 0x54, 0x31, 0xc3, 0x3e, 0x9d, 0x1a, 0x83, 0x84, 0x18,
22+
0x51, 0xe9, 0x8c, 0x24, 0xcf, 0xac, 0xc6, 0x0d, 0x26, 0x2c, 0x9f, 0x2b, 0xd5, 0x91, 0x98,
23+
0x89, 0xe3, 0x68, 0x97, 0x36, 0x02, 0xec, 0x16, 0x37, 0x24, 0x08, 0xb4, 0x77, 0xd1, 0x56,
24+
0x10, 0x3e, 0xf0, 0x64, 0xf6, 0x68, 0x50, 0x68, 0x31, 0xf8, 0x9b, 0x88, 0xf2, 0xc5, 0xfb,
25+
0xc9, 0x21, 0xd2, 0xdf, 0x93, 0x6f, 0x98, 0x94, 0x53, 0x68, 0xe5, 0x25, 0x8d, 0x8a, 0xf1,
26+
0xd7, 0x5b, 0xf3, 0xf9, 0xdf, 0x8c, 0x77, 0x24, 0x9e, 0x28, 0x09, 0x36, 0xf0, 0xa2, 0x93,
27+
0x17, 0xad, 0xbb, 0x1a, 0xd7, 0x6f, 0x25, 0x6b, 0x0c, 0xd3, 0x76, 0x7f, 0xcf, 0x3a, 0xe3,
28+
0x1a, 0x84, 0x57, 0x62, 0x71, 0x8a, 0x6a, 0x42, 0x94, 0x71, 0x21, 0x6a, 0x13, 0x73, 0x17,
29+
0x56, 0xa2, 0x38, 0xc1, 0x5e, 0x76, 0x0b, 0x67, 0x6b, 0x6e, 0xcd, 0xd3, 0xe2, 0x8a, 0x80,
30+
0x61, 0x6c, 0x1c, 0x60, 0x9d, 0x65, 0xbd, 0x5a, 0x4e, 0xeb, 0xa2, 0x06, 0xd6, 0xbe, 0xf5,
31+
0x49, 0xc1, 0x7d, 0xd9, 0x46, 0x3e, 0x9f, 0x2f, 0x92, 0xa4, 0x1a, 0x14, 0x2c, 0x1e, 0xb7,
32+
0x6d, 0x71, 0x29, 0x92, 0x43, 0x7b, 0x76, 0xa4, 0x8b, 0x33, 0xf3, 0xd0, 0xda, 0x7c, 0x7f,
33+
0x73, 0x50, 0xe2, 0xc5, 0x30, 0xad, 0x9e, 0x0f, 0x61, 0x73, 0xa0, 0xbb, 0x87, 0x1f, 0x0b,
34+
0x70, 0xa9, 0xa6, 0xaa, 0x31, 0x2d, 0x62, 0x2c, 0xaf, 0xea, 0x49, 0xb2, 0xce, 0x6c, 0x23,
35+
0x90, 0xdd, 0x29, 0x37, 0x67, 0xb1, 0xc9, 0x99, 0x3a, 0x3f, 0xa6, 0x69, 0xc9, 0x0d, 0x24,
36+
0x3f,
37+
];
38+
39+
pub fn get_ext_rsa_pub() -> Public {
40+
let object_attributes = ObjectAttributesBuilder::new()
41+
.with_user_with_auth(true)
42+
.with_decrypt(false)
43+
.with_sign_encrypt(true)
44+
.with_restricted(false)
45+
.build()
46+
.expect("Failed to build object attributes");
47+
48+
PublicBuilder::new()
49+
.with_public_algorithm(PublicAlgorithm::Rsa)
50+
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
51+
.with_object_attributes(object_attributes)
52+
.with_rsa_parameters(
53+
PublicRsaParametersBuilder::new_unrestricted_signing_key(
54+
RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
55+
.expect("Failed to create rsa scheme"),
56+
RsaKeyBits::Rsa2048,
57+
Default::default(), // Default exponent is 0 but TPM internally this is mapped to 65537
58+
)
59+
.build()
60+
.expect("Failed to create rsa parameters for public structure"),
61+
)
62+
.with_rsa_unique_identifier(
63+
PublicKeyRsa::try_from(&RSA_KEY[..])
64+
.expect("Failed to create Public RSA key from buffer"),
65+
)
66+
.build()
67+
.expect("Failed to build Public structure")
68+
}
69+
70+
#[test]
71+
fn test_public_to_decoded_key_rsa() {
72+
let public_rsa = get_ext_rsa_pub();
73+
let default_exponent = IntegerAsn1::from_bytes_be_signed(65537_u32.to_be_bytes().to_vec());
74+
let decoded_key = DecodedKey::try_from(public_rsa)
75+
.expect("Failed to convert Public structure to DecodedKey (RSA).");
76+
match decoded_key {
77+
DecodedKey::RsaPublicKey(key) => {
78+
assert_eq!(
79+
key.public_exponent, default_exponent,
80+
"RSA exponents are not equal."
81+
);
82+
assert_eq!(key.modulus.as_unsigned_bytes_be(), RSA_KEY);
83+
}
84+
DecodedKey::EcPoint(..) => panic!("RSA key was decoded to EcPoint!"),
85+
}
86+
}
87+
88+
#[test]
89+
fn test_public_to_subject_public_key_info_rsa() {
90+
let public_rsa = get_ext_rsa_pub();
91+
let default_exponent = IntegerAsn1::from_bytes_be_signed(65537_u32.to_be_bytes().to_vec());
92+
let key = SubjectPublicKeyInfo::try_from(public_rsa)
93+
.expect("Failed to convert Public structure to SubjectPublicKeyInfo (RSA).");
94+
assert_eq!(key.algorithm, AlgorithmIdentifier::new_rsa_encryption());
95+
match key.subject_public_key {
96+
PublicKey::Rsa(key) => {
97+
assert_eq!(key.public_exponent, default_exponent);
98+
assert_eq!(key.modulus.as_unsigned_bytes_be(), RSA_KEY)
99+
}
100+
_ => panic!("PublicKey of SubjectPublicKeyInfo is not an instance for RSA"),
101+
}
102+
}
103+
}
104+
105+
mod public_ecc_test {
106+
use picky_asn1::bit_string::BitString;
107+
use picky_asn1_x509::{AlgorithmIdentifier, EcParameters, PublicKey, SubjectPublicKeyInfo};
108+
use std::convert::TryFrom;
109+
use tss_esapi::{
110+
abstraction::public::DecodedKey,
111+
attributes::ObjectAttributesBuilder,
112+
interface_types::{
113+
algorithm::{HashingAlgorithm, PublicAlgorithm},
114+
ecc::EccCurve,
115+
},
116+
structures::{
117+
EccParameter, EccPoint, EccScheme, KeyDerivationFunctionScheme, Public, PublicBuilder,
118+
PublicEccParametersBuilder,
119+
},
120+
};
121+
122+
const EC_POINT: [u8; 65] = [
123+
0x04, 0x14, 0xd8, 0x59, 0xec, 0x31, 0xe5, 0x94, 0x0f, 0x2b, 0x3a, 0x08, 0x97, 0x64, 0xc4,
124+
0xfb, 0xa6, 0xcd, 0xaf, 0x0e, 0xa2, 0x44, 0x7f, 0x30, 0xcf, 0xe8, 0x2e, 0xe5, 0x1b, 0x47,
125+
0x70, 0x01, 0xc3, 0xd6, 0xb4, 0x69, 0x7e, 0xa1, 0xcf, 0x03, 0xdb, 0x05, 0x9c, 0x62, 0x3e,
126+
0xc6, 0x15, 0x4f, 0xed, 0xab, 0xa0, 0xa0, 0xab, 0x84, 0x2e, 0x67, 0x0c, 0x98, 0xc7, 0x1e,
127+
0xef, 0xd2, 0x51, 0x91, 0xce,
128+
];
129+
130+
pub fn get_ecc_point() -> EccPoint {
131+
let x =
132+
EccParameter::try_from(&EC_POINT[1..33]).expect("Failed to construct x EccParameter");
133+
let y: EccParameter =
134+
EccParameter::try_from(&EC_POINT[33..]).expect("Failed to construct y EccParameter");
135+
EccPoint::new(x, y)
136+
}
137+
138+
pub fn get_ext_ecc_pub() -> Public {
139+
let object_attributes = ObjectAttributesBuilder::new()
140+
.with_user_with_auth(true)
141+
.with_decrypt(false)
142+
.with_sign_encrypt(true)
143+
.with_restricted(false)
144+
.build()
145+
.expect("Failed to build object attributes");
146+
147+
let ecc_parameters = PublicEccParametersBuilder::new()
148+
.with_ecc_scheme(EccScheme::Null)
149+
.with_curve(EccCurve::NistP256)
150+
.with_is_signing_key(false)
151+
.with_is_decryption_key(true)
152+
.with_restricted(false)
153+
.with_key_derivation_function_scheme(KeyDerivationFunctionScheme::Null)
154+
.build()
155+
.expect("Failed to build PublicEccParameters");
156+
PublicBuilder::new()
157+
.with_public_algorithm(PublicAlgorithm::Ecc)
158+
.with_name_hashing_algorithm(HashingAlgorithm::Sha256)
159+
.with_object_attributes(object_attributes)
160+
.with_ecc_parameters(ecc_parameters)
161+
.with_ecc_unique_identifier(get_ecc_point())
162+
.build()
163+
.expect("Failed to build Public structure")
164+
}
165+
166+
#[test]
167+
fn test_public_to_decoded_key_ecc() {
168+
let public_ecc = get_ext_ecc_pub();
169+
let decoded_key = DecodedKey::try_from(public_ecc)
170+
.expect("Failed to convert Public structure to DecodedKey (ECC).");
171+
172+
match decoded_key {
173+
DecodedKey::RsaPublicKey(..) => panic!("ECC key was decoded to RsaPublicKey!"),
174+
DecodedKey::EcPoint(ec_point) => {
175+
assert_eq!(ec_point.to_vec(), EC_POINT.to_vec());
176+
}
177+
}
178+
}
179+
180+
#[test]
181+
fn test_public_to_subject_public_key_info_ecc() {
182+
let public_ecc = get_ext_ecc_pub();
183+
let key = SubjectPublicKeyInfo::try_from(public_ecc)
184+
.expect("Failed to convert Public structure to SubjectPublicKeyInfo (ECC).");
185+
assert_eq!(
186+
key.algorithm,
187+
AlgorithmIdentifier::new_elliptic_curve(EcParameters::NamedCurve(
188+
picky_asn1_x509::oids::secp256r1().into()
189+
))
190+
);
191+
match key.subject_public_key {
192+
PublicKey::Ec(ec_point) => {
193+
let ec_point_bitstring: BitString = ec_point.into();
194+
let ec_point_vec: Vec<u8> = ec_point_bitstring.into();
195+
assert_eq!(ec_point_vec, EC_POINT.to_vec());
196+
}
197+
_ => panic!("PublicKey of SubjectPublicKeyInfo is not an instance for ECC"),
198+
}
199+
}
200+
}

0 commit comments

Comments
 (0)