-
-
Notifications
You must be signed in to change notification settings - Fork 203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TlsStream::certificate_chain, ChainIterator type and Certificate::public_key_info_der #117
base: master
Are you sure you want to change the base?
Changes from 10 commits
c3e2615
d5caf12
3982c58
c978e57
6de69bd
ee90227
bdfb49e
7c44234
5777ab2
cc76bf1
0cf0372
d3a6925
05cc92f
fec0d51
07ac327
bfbbb0f
6c441af
deefe6b
38aa40e
3016bc2
ff5fb62
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
extern crate schannel; | ||
|
||
use self::schannel::cert_context::{CertContext, HashAlgorithm}; | ||
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions}; | ||
use self::schannel::cert_store::{CertAdd, CertStore, Certs, Memory, PfxImportOptions}; | ||
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred}; | ||
use self::schannel::tls_stream; | ||
use std::error; | ||
|
@@ -89,7 +89,8 @@ impl Identity { | |
return Err(io::Error::new( | ||
io::ErrorKind::InvalidInput, | ||
"No identity found in PKCS #12 archive", | ||
).into()); | ||
) | ||
.into()); | ||
} | ||
}; | ||
|
||
|
@@ -115,13 +116,18 @@ impl Certificate { | |
Err(_) => Err(io::Error::new( | ||
io::ErrorKind::InvalidInput, | ||
"PEM representation contains non-UTF-8 bytes", | ||
).into()), | ||
) | ||
.into()), | ||
} | ||
} | ||
|
||
pub fn to_der(&self) -> Result<Vec<u8>, Error> { | ||
Ok(self.0.to_der().to_vec()) | ||
} | ||
|
||
pub fn public_key_info_der(&self) -> Result<Vec<u8>, Error> { | ||
Ok(self.0.subject_public_key_info_der()?) | ||
} | ||
} | ||
|
||
pub struct MidHandshakeTlsStream<S>(tls_stream::MidHandshakeTlsStream<S>); | ||
|
@@ -149,7 +155,7 @@ where | |
|
||
pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> { | ||
match self.0.handshake() { | ||
Ok(s) => Ok(TlsStream(s)), | ||
Ok(s) => Ok(TlsStream(s, None)), | ||
Err(e) => Err(e.into()), | ||
} | ||
} | ||
|
@@ -227,7 +233,7 @@ impl TlsConnector { | |
builder.verify_callback(|_| Ok(())); | ||
} | ||
match builder.connect(cred, stream) { | ||
Ok(s) => Ok(TlsStream(s)), | ||
Ok(s) => Ok(TlsStream(s, None)), | ||
Err(e) => Err(e.into()), | ||
} | ||
} | ||
|
@@ -259,13 +265,28 @@ impl TlsAcceptor { | |
// FIXME we're probably missing the certificate chain? | ||
let cred = builder.acquire(Direction::Inbound)?; | ||
match tls_stream::Builder::new().accept(cred, stream) { | ||
Ok(s) => Ok(TlsStream(s)), | ||
Ok(s) => Ok(TlsStream(s, None)), | ||
Err(e) => Err(e.into()), | ||
} | ||
} | ||
} | ||
|
||
pub struct TlsStream<S>(tls_stream::TlsStream<S>); | ||
pub struct ChainIterator<'a, S: 'a> { | ||
certs: Option<Certs<'a>>, | ||
_stream: &'a TlsStream<S>, | ||
} | ||
impl<'a, S> Iterator for ChainIterator<'a, S> { | ||
type Item = Certificate; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
if let Some(certs) = self.certs.as_mut() { | ||
return certs.next().map(Certificate); | ||
} | ||
None | ||
} | ||
} | ||
|
||
pub struct TlsStream<S>(tls_stream::TlsStream<S>, Option<CertStore>); | ||
|
||
impl<S: fmt::Debug> fmt::Debug for TlsStream<S> { | ||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
|
@@ -294,6 +315,24 @@ impl<S: io::Read + io::Write> TlsStream<S> { | |
} | ||
} | ||
|
||
pub fn certificate_chain(&mut self) -> Result<ChainIterator<S>, Error> { | ||
if self.1.is_none() { | ||
match self.0.peer_certificate() { | ||
Ok(cert) => { | ||
self.1 = cert.cert_store(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to cache the cert store? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could not find a better solution. CertStore once you call certs is borrowed. You can't drop it and you can't put it in the iterator as it will be a self referenced struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like things would be easier to work with if we just returned a |
||
} | ||
Err(ref e) if e.raw_os_error() == Some(SEC_E_NO_CREDENTIALS as i32) => { | ||
self.1 = None; | ||
} | ||
Err(e) => return Err(Error(e)), | ||
} | ||
} | ||
Ok(ChainIterator { | ||
certs: self.1.as_ref().map(|c| c.certs()), | ||
_stream: self, | ||
}) | ||
} | ||
|
||
pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, Error> { | ||
let cert = if self.0.is_server() { | ||
self.0.certificate() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,24 @@ | ||
extern crate core_foundation; | ||
extern crate core_foundation_sys; | ||
extern crate libc; | ||
extern crate security_framework; | ||
extern crate security_framework_sys; | ||
extern crate tempfile; | ||
|
||
use self::security_framework::base; | ||
use self::security_framework::certificate::SecCertificate; | ||
use self::security_framework::identity::SecIdentity; | ||
use self::security_framework::import_export::{ImportedIdentity, Pkcs12ImportOptions}; | ||
use self::security_framework::secure_transport::{ | ||
self, ClientBuilder, SslConnectionType, SslContext, SslProtocol, SslProtocolSide, | ||
}; | ||
use self::security_framework::{base, trust::SecTrust}; | ||
use self::security_framework_sys::base::errSecIO; | ||
|
||
use self::tempfile::TempDir; | ||
use std::error; | ||
use std::fmt; | ||
use std::io; | ||
|
||
use std::sync::Mutex; | ||
use std::sync::{Once, ONCE_INIT}; | ||
|
||
|
@@ -174,6 +178,10 @@ impl Certificate { | |
pub fn to_der(&self) -> Result<Vec<u8>, Error> { | ||
Ok(self.0.to_der()) | ||
} | ||
|
||
pub fn public_key_info_der(&self) -> Result<Vec<u8>, Error> { | ||
Ok(self.0.public_key_info_der()?.unwrap_or(Vec::new())) | ||
} | ||
} | ||
|
||
pub enum HandshakeError<S> { | ||
|
@@ -351,6 +359,24 @@ impl TlsAcceptor { | |
} | ||
} | ||
|
||
pub struct ChainIterator<'a, S: 'a> { | ||
trust: Option<SecTrust>, | ||
pos: usize, | ||
_stream: &'a TlsStream<S>, | ||
} | ||
impl<'a, S> Iterator for ChainIterator<'a, S> { | ||
type Item = Certificate; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
if let Some(trust) = self.trust.as_ref() { | ||
let pos = self.pos; | ||
self.pos += 1; | ||
return trust.certificate_at_index(pos as _).map(Certificate); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to stop at the end of the chain, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trust will return None once it is over limit. |
||
} | ||
None | ||
} | ||
} | ||
|
||
pub struct TlsStream<S> { | ||
stream: secure_transport::SslStream<S>, | ||
cert: Option<SecCertificate>, | ||
|
@@ -385,6 +411,25 @@ impl<S: io::Read + io::Write> TlsStream<S> { | |
Ok(trust.certificate_at_index(0).map(Certificate)) | ||
} | ||
|
||
pub fn certificate_chain(&mut self) -> Result<ChainIterator<S>, Error> { | ||
let trust = match self.stream.context().peer_trust2()? { | ||
Some(trust) => trust, | ||
None => { | ||
return Ok(ChainIterator { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be restructured a bit to avoid having to have a separate early return: let trust = match self.stream.context().peer_trust2()? {
Some(trust) => {
trust.evaluate()?;
Some(trust)
}
None => None
};
... |
||
trust: None, | ||
pos: 0, | ||
_stream: self, | ||
}); | ||
} | ||
}; | ||
trust.evaluate()?; | ||
Ok(ChainIterator { | ||
trust: Some(trust), | ||
pos: 0, | ||
_stream: self, | ||
}) | ||
} | ||
|
||
#[cfg(target_os = "ios")] | ||
pub fn tls_server_end_point(&self) -> Result<Option<Vec<u8>>, Error> { | ||
Ok(None) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -206,6 +206,23 @@ impl Certificate { | |
let der = self.0.to_der()?; | ||
Ok(der) | ||
} | ||
|
||
/// Returns der encoded subjectPublicKeyInfo. | ||
pub fn public_key_info_der(&self) -> Result<Vec<u8>> { | ||
let der = self.0.public_key_info_der()?; | ||
Ok(der) | ||
} | ||
} | ||
|
||
/// An iterator over a certificate chain. | ||
pub struct ChainIterator<'a, S: 'a>(imp::ChainIterator<'a, S>); | ||
|
||
impl<'a, S> Iterator for ChainIterator<'a, S> { | ||
type Item = Certificate; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
self.0.next().map(Certificate) | ||
} | ||
} | ||
|
||
/// A TLS stream which has been interrupted midway through the handshake process. | ||
|
@@ -630,6 +647,11 @@ impl<S: io::Read + io::Write> TlsStream<S> { | |
Ok(self.0.peer_certificate()?.map(Certificate)) | ||
} | ||
|
||
/// Returns an iterator over certificate chain, if available. | ||
pub fn certificate_chain(&mut self) -> Result<ChainIterator<S>> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we distinguish the "no chain present" case here by returning something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to create an empty iterator that just returns None immediately. That is what happens in the downstream crates. Result<Option> is a pretty annoying API. |
||
Ok(ChainIterator(self.0.certificate_chain()?)) | ||
} | ||
|
||
/// Returns the tls-server-end-point channel binding data as defined in [RFC 5929]. | ||
/// | ||
/// [RFC 5929]: https://tools.ietf.org/html/rfc5929 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use a struct here with named fields to make things a bit more clear.