| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| //! This module implements ECDSA (NIST P-256 curve) keys related functions. You |
| //! can export private key to a DER format or create a certificate with |
| //! extension for TLS-based remote attestation. |
| |
| use std::prelude::v1::*; |
| |
| use anyhow::Result; |
| use sgx_tcrypto::SgxEccHandle; |
| use sgx_types::{sgx_ec256_private_t, sgx_ec256_public_t}; |
| |
| /// Validation days of cert for TLS connection. |
| const CERT_VALID_DAYS: i64 = 90i64; |
| |
| /// NistP256KeyPair stores a pair of ECDSA (private, public) key based on the |
| /// NIST P-256 curve (a.k.a secp256r1). |
| pub(crate) struct NistP256KeyPair { |
| prv_k: sgx_ec256_private_t, |
| pub_k: sgx_ec256_public_t, |
| } |
| |
| impl NistP256KeyPair { |
| /// Generate a ECDSA key pair. |
| pub(crate) fn new() -> Result<Self> { |
| let ecc_handle = SgxEccHandle::new(); |
| ecc_handle.open()?; |
| let (prv_k, pub_k) = ecc_handle.create_key_pair()?; |
| ecc_handle.close()?; |
| Ok(Self { prv_k, pub_k }) |
| } |
| |
| pub(crate) fn pub_k(&self) -> sgx_ec256_public_t { |
| self.pub_k |
| } |
| |
| pub(crate) fn private_key_into_der(&self) -> Vec<u8> { |
| use bit_vec::BitVec; |
| use yasna::construct_der; |
| use yasna::models::ObjectIdentifier; |
| use yasna::Tag; |
| |
| // Construct useful OIDs. |
| let ec_public_key_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 2, 1]); |
| let prime256v1_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 3, 1, 7]); |
| |
| let pub_key_bytes = self.public_key_into_bytes(); |
| let prv_key_bytes = self.private_key_into_bytes(); |
| |
| // Construct private key in DER. |
| construct_der(|writer| { |
| writer.write_sequence(|writer| { |
| writer.next().write_u8(0); |
| writer.next().write_sequence(|writer| { |
| writer.next().write_oid(&ec_public_key_oid); |
| writer.next().write_oid(&prime256v1_oid); |
| }); |
| let inner_key_der = construct_der(|writer| { |
| writer.write_sequence(|writer| { |
| writer.next().write_u8(1); |
| writer.next().write_bytes(&prv_key_bytes); |
| writer.next().write_tagged(Tag::context(1), |writer| { |
| writer.write_bitvec(&BitVec::from_bytes(&pub_key_bytes)); |
| }); |
| }); |
| }); |
| writer.next().write_bytes(&inner_key_der); |
| }); |
| }) |
| } |
| |
| /// create_cert_with_extension makes a self-signed x509-v3 cert with SGX |
| /// attestation report as extensions. |
| /// @reference [Internet X.509 Public Key Infrastructure Certificate and |
| /// Certificate Revocation List (CRL) Profile][1] |
| /// |
| /// [1]: https://tools.ietf.org/pdf/rfc5280.pdf |
| pub(crate) fn create_cert_with_extension( |
| &self, |
| issuer: &str, |
| subject: &str, |
| payload: &[u8], |
| ) -> Vec<u8> { |
| use crate::cert::*; |
| use bit_vec::BitVec; |
| use chrono::TimeZone; |
| use num_bigint::BigUint; |
| use std::time::SystemTime; |
| use std::time::UNIX_EPOCH; |
| use std::untrusted::time::SystemTimeEx; |
| use yasna::construct_der; |
| use yasna::models::{ObjectIdentifier, UTCTime}; |
| |
| // Construct useful OIDs. |
| let ecdsa_with_sha256_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 4, 3, 2]); |
| let common_name_oid = ObjectIdentifier::from_slice(&[2, 5, 4, 3]); |
| let ec_public_key_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 2, 1]); |
| let prime256v1_oid = ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 3, 1, 7]); |
| let comment_oid = ObjectIdentifier::from_slice(&[2, 16, 840, 1, 113_730, 1, 13]); |
| |
| let pub_key_bytes = self.public_key_into_bytes(); |
| |
| // UNIX_EPOCH is the earliest time stamp. This unwrap should constantly succeed. |
| let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); |
| let issue_ts = chrono::Utc.timestamp(now.as_secs() as i64, 0); |
| |
| // This is guaranteed to be a valid duration. |
| let expire = now + chrono::Duration::days(CERT_VALID_DAYS).to_std().unwrap(); |
| let expire_ts = chrono::Utc.timestamp(expire.as_secs() as i64, 0); |
| |
| // Construct certificate with payload in extension in DER. |
| let tbs_cert_der = construct_der(|writer| { |
| let version = 2i8; |
| let serial = 1u8; |
| let cert_sign_algo = asn1_seq!(ecdsa_with_sha256_oid.clone()); |
| let issuer = asn1_seq!(asn1_seq!(asn1_seq!( |
| common_name_oid.clone(), |
| issuer.to_owned() |
| ))); |
| let valid_range = asn1_seq!( |
| UTCTime::from_datetime(&issue_ts), |
| UTCTime::from_datetime(&expire_ts), |
| ); |
| let subject = asn1_seq!(asn1_seq!(asn1_seq!( |
| common_name_oid.clone(), |
| subject.to_string(), |
| ))); |
| let pub_key = asn1_seq!( |
| asn1_seq!(ec_public_key_oid, prime256v1_oid,), |
| BitVec::from_bytes(&pub_key_bytes), |
| ); |
| let sgx_ra_cert_ext = asn1_seq!(asn1_seq!(comment_oid, payload.to_owned())); |
| let tbs_cert = asn1_seq!( |
| version, |
| serial, |
| cert_sign_algo, |
| issuer, |
| valid_range, |
| subject, |
| pub_key, |
| sgx_ra_cert_ext, |
| ); |
| TbsCert::dump(writer, tbs_cert); |
| }); |
| |
| // There will be serious problems if this call fails. We might as well |
| // panic in this case, thus unwrap() |
| let ecc_handle = SgxEccHandle::new(); |
| ecc_handle.open().unwrap(); |
| |
| let sig = ecc_handle |
| .ecdsa_sign_slice(&tbs_cert_der.as_slice(), &self.prv_k) |
| .unwrap(); |
| |
| ecc_handle.close().unwrap(); |
| |
| let sig_der = yasna::construct_der(|writer| { |
| writer.write_sequence(|writer| { |
| let mut sig_x = sig.x; |
| sig_x.reverse(); |
| let mut sig_y = sig.y; |
| sig_y.reverse(); |
| writer.next().write_biguint(&BigUint::from_slice(&sig_x)); |
| writer.next().write_biguint(&BigUint::from_slice(&sig_y)); |
| }); |
| }); |
| |
| yasna::construct_der(|writer| { |
| writer.write_sequence(|writer| { |
| writer.next().write_der(&tbs_cert_der.as_slice()); |
| CertSignAlgo::dump(writer.next(), asn1_seq!(ecdsa_with_sha256_oid.clone())); |
| writer |
| .next() |
| .write_bitvec(&BitVec::from_bytes(&sig_der.as_slice())); |
| }); |
| }) |
| } |
| |
| fn public_key_into_bytes(&self) -> Vec<u8> { |
| // The first byte must be 4, which indicates the uncompressed encoding. |
| let mut pub_key_bytes: Vec<u8> = vec![4]; |
| pub_key_bytes.extend(self.pub_k.gx.iter().rev()); |
| pub_key_bytes.extend(self.pub_k.gy.iter().rev()); |
| pub_key_bytes |
| } |
| |
| fn private_key_into_bytes(&self) -> Vec<u8> { |
| let mut prv_key_bytes: Vec<u8> = vec![]; |
| prv_key_bytes.extend(self.prv_k.r.iter().rev()); |
| prv_key_bytes |
| } |
| } |