-
Notifications
You must be signed in to change notification settings - Fork 92
Allocation-free operations #349
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -3,29 +3,38 @@ | |
| //! Signing and authentication functions | ||
|
|
||
| use crate::context::Function; | ||
| #[cfg(doc)] | ||
| use crate::error::RvError; | ||
| use crate::error::{Result, Rv}; | ||
| use crate::mechanism::Mechanism; | ||
| use crate::object::ObjectHandle; | ||
| use crate::session::Session; | ||
| use cryptoki_sys::*; | ||
| use std::convert::TryInto; | ||
|
|
||
| /// # Generating and Verifying Signatures | ||
| /// | ||
| /// Several functions are provided for signing data and verifying signatures. | ||
| /// This includes message authentication codes (MACs). The signed data can be | ||
| /// provided in one-shot and streaming modes. | ||
| impl Session { | ||
| /// Sign data in single-part | ||
| /// Sign data in one-shot mode. | ||
| /// | ||
| /// `data` should be a byte sequence representing the input message. It will | ||
| /// be signed using the specified key, and the resulting signature will be | ||
| /// returned in a `Vec`. | ||
| /// | ||
| /// Use [`Self::sign_into()`] if (an upper bound for) the size of the | ||
| /// signature is known, to avoid the heap allocation of `Vec`. Use | ||
| /// [`Self::sign_init()`] etc. if the input data is being streamed (i.e. it | ||
| /// is not all immediately available). | ||
| pub fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result<Vec<u8>> { | ||
| let mut mechanism: CK_MECHANISM = mechanism.into(); | ||
| let mut signature_len = 0; | ||
|
|
||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_SignInit)( | ||
| self.handle(), | ||
| &mut mechanism as CK_MECHANISM_PTR, | ||
| key.handle(), | ||
| )) | ||
| .into_result(Function::SignInit)?; | ||
| } | ||
| // Initialize the signing operation. | ||
| self.sign_init(mechanism, key)?; | ||
|
|
||
| // Get the output buffer length | ||
| // Get the output buffer length. | ||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_Sign)( | ||
| self.handle(), | ||
|
|
@@ -37,9 +46,10 @@ impl Session { | |
| .into_result(Function::Sign)?; | ||
| } | ||
|
|
||
| // Allocate the output buffer. | ||
| let mut signature = vec![0; signature_len.try_into()?]; | ||
|
|
||
| //TODO: we should add a new error instead of those unwrap! | ||
| // Perform the actual signing. | ||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_Sign)( | ||
| self.handle(), | ||
|
|
@@ -51,11 +61,68 @@ impl Session { | |
| .into_result(Function::Sign)?; | ||
| } | ||
|
|
||
| // Limit the output buffer to the size of the generated signature. | ||
| signature.truncate(signature_len.try_into()?); | ||
|
|
||
| Ok(signature) | ||
| } | ||
|
|
||
| /// Sign data into the given buffer. | ||
| /// | ||
| /// `data` should be a byte sequence representing the input message. It will | ||
| /// be signed using the specified key, and the resulting signature will be | ||
| /// written to the buffer `sig`. | ||
| /// | ||
| /// `sig` should be large enough to store the signature. The number of | ||
| /// filled bytes will be returned, such that `sig[0..size]` contains the | ||
| /// prepared signature. `sig[size..]` might be modified. | ||
| /// | ||
| /// Use [`Self::sign()`] if (an upper bound for) the size of the signature | ||
|
wiktor-k marked this conversation as resolved.
|
||
| /// is not known. Use [`Self::sign_init()`] etc. if the input data is being | ||
| /// streamed (i.e. it is not all immediately available). | ||
| /// | ||
| /// ## Errors | ||
| /// | ||
| /// Returns [`RvError::BufferTooSmall`] if the generated signature does not | ||
| /// fit in `sig`. `sig` might be modified. The size of the actual signature | ||
| /// is **not** returned. This method should only be used if the caller knows | ||
| /// an upper bound for the signature size. | ||
| pub fn sign_into( | ||
| &self, | ||
| mechanism: &Mechanism, | ||
| key: ObjectHandle, | ||
| data: &[u8], | ||
| sig: &mut [u8], | ||
| ) -> Result<usize> { | ||
| // The size of the signature buffer, into which 'C_Sign' will write the | ||
| // size of the generated signature. | ||
| let sig_buf_len = sig.len().try_into()?; | ||
| let mut sig_len = sig_buf_len; | ||
|
|
||
| // Initialize the signing operation. | ||
| self.sign_init(mechanism, key)?; | ||
|
|
||
| // Perform the actual signing. | ||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_Sign)( | ||
| self.handle(), | ||
| data.as_ptr() as *mut u8, | ||
| data.len().try_into()?, | ||
| sig.as_mut_ptr(), | ||
| &mut sig_len, | ||
| )) | ||
| .into_result(Function::Sign)?; | ||
| } | ||
|
|
||
| assert!( | ||
| sig_len <= sig_buf_len, | ||
| "'C_Sign' succeeded but increased 'sig_len', possibly indicating out-of-bounds accesses" | ||
| ); | ||
|
Comment on lines
+117
to
+120
Collaborator
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 think we should not use
Contributor
Author
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 initially had a TODO comment here discussing whether an
Member
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 don't think that PKCS11 implementation should increase the So I would also be in favor of removing those asserts!
Contributor
Author
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. My only concern is that user code will assume this property to be upheld. It would be better for
Collaborator
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. AFAIK Rust still inserts bounds checks in release mode (source https://news.ycombinator.com/item?id=39261604) unless the optimizer can deduct that it's safe to remove (eg. in for loops). Maybe yet another possibility would be to use
Collaborator
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.
Yeah, asserts in production code are especially tricky. For really rare occasions even stdlib has panics but maybe we can "have a cookie and eat it too" here by introducing some error variant with just a string description that'd be a catch-all for all these assert-like bugs? 🤔
Member
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 think that is fair to assume (we can even write a warning notice about that in the readme). If we can't trust that the PKCS11 library implements the spec correctly then there is not much we can do 😢
Contributor
Author
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 normally agree, but I think "we think UB has occurred, but we don't want to panic, maybe everything is fine" is concerning. I don't know what the ideal solution is. I'm happy to implement whatever you (the maintainers) think should happen.
Collaborator
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.
That's great, we just have a little split brain problem here so it may take a while until we have a consensus:) Personally I'm okay with an assert of a very unlikely scenario. I'm also fine with a new Error enum variant if that makes @Jakuje calm. The "no check at all" scenario is the worse one IMO.
Collaborator
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 agree so don't let me hold the PR back as it is a great improvement! I agree that this case really means the underlying token is broken so maybe the assert is adequate. I just know how annoying is when somewhere deep in the dependency chain of your built package you get assert and there is nothing you can do but rebuild. |
||
|
|
||
| // NOTE: As checked above, 'sig_len <= sig_buf_len <= usize::MAX'. | ||
| Ok(sig_len as usize) | ||
| } | ||
|
|
||
| /// Starts new multi-part signing operation | ||
| pub fn sign_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { | ||
| let mut mechanism: CK_MECHANISM = mechanism.into(); | ||
|
|
@@ -87,12 +154,20 @@ impl Session { | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Finalizes ongoing multi-part signing operation, | ||
| /// returning the signature | ||
| /// Complete an ongoing streaming signing operation. | ||
| /// | ||
| /// This must be preceded by [`Self::sign_init()`] and zero or more calls | ||
| /// to [`Self::sign_update()`]. This method will terminate the multi-part | ||
| /// signing operation. The resulting signature will be returned in a `Vec`. | ||
| /// | ||
| /// Use [`Self::sign_final_into()`] if (an upper bound for) the size of | ||
| /// the signature is known, to avoid the heap allocation of `Vec`. Use | ||
| /// [`Self::sign()`] if the input data is entirely available in a single | ||
| /// buffer (i.e. does not have to be streamed). | ||
| pub fn sign_final(&self) -> Result<Vec<u8>> { | ||
| let mut signature_len = 0; | ||
|
|
||
| // Get the output buffer length | ||
| // Get the output buffer length. | ||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_SignFinal)( | ||
| self.handle(), | ||
|
|
@@ -102,8 +177,10 @@ impl Session { | |
| .into_result(Function::SignFinal)?; | ||
| } | ||
|
|
||
| // Allocate the output buffer. | ||
| let mut signature = vec![0; signature_len.try_into()?]; | ||
|
|
||
| // Perform the actual signing. | ||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_SignFinal)( | ||
| self.handle(), | ||
|
|
@@ -113,11 +190,59 @@ impl Session { | |
| .into_result(Function::SignFinal)?; | ||
| } | ||
|
|
||
| // Limit the output buffer to the size of the generated signature. | ||
| signature.truncate(signature_len.try_into()?); | ||
|
|
||
| Ok(signature) | ||
| } | ||
|
|
||
| /// Complete an ongoing multi-part signing operation, writing the signature | ||
| /// into the given buffer. | ||
| /// | ||
| /// This must be preceded by [`Self::sign_init()`] and zero or more calls | ||
| /// to [`Self::sign_update()`]. This method will terminate the multi-part | ||
| /// signing operation, and write the signature to the buffer `sig`. | ||
| /// | ||
| /// `sig` should be large enough to store the signature. The number of | ||
| /// filled bytes will be returned, such that `sig[0..size]` contains the | ||
| /// prepared signature. `sig[size..]` might be modified. | ||
| /// | ||
| /// Use [`Self::sign_final()`] if (an upper bound for) the size of the | ||
| /// signature is not known. Use [`Self::sign_into()`] if the input data | ||
| /// is entirely available in a single buffer (i.e. does not have to be | ||
| /// streamed). | ||
| /// | ||
| /// ## Errors | ||
| /// | ||
| /// Returns [`RvError::BufferTooSmall`] if the generated signature does not | ||
| /// fit in `sig`. `sig` might be modified. The size of the actual signature | ||
| /// is **not** returned. This method should only be used if the caller knows | ||
| /// an upper bound for the signature size. | ||
| pub fn sign_final_into(&self, sig: &mut [u8]) -> Result<usize> { | ||
| // The size of the signature buffer, into which 'C_SignFinal' will write | ||
| // the size of the generated signature. | ||
| let sig_buf_len = sig.len().try_into()?; | ||
| let mut sig_len = sig_buf_len; | ||
|
|
||
| // Perform the underlying finalization. | ||
| unsafe { | ||
| Rv::from(get_pkcs11!(self.client(), C_SignFinal)( | ||
| self.handle(), | ||
| sig.as_mut_ptr(), | ||
| &mut sig_len, | ||
| )) | ||
| .into_result(Function::SignFinal)?; | ||
| } | ||
|
|
||
| assert!( | ||
| sig_len <= sig_buf_len, | ||
| "'C_SignFinal' succeeded but increased 'sig_len', possibly indicating out-of-bounds accesses" | ||
| ); | ||
|
|
||
| // NOTE: As checked above, 'sig_len <= sig_buf_len <= usize::MAX'. | ||
| Ok(sig_len as usize) | ||
| } | ||
|
|
||
| /// Verify data in single-part | ||
| pub fn verify( | ||
| &self, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.