flexiber-0.1.0/.cargo_vcs_info.json0000644000000001120000000000100126240ustar { "git": { "sha1": "825eba57df47a8d084e185eca58912caf231df31" } } flexiber-0.1.0/.github/workflows/ci.yml000064400000000000000000000012430072674642500161650ustar 00000000000000name: CI on: push: branches: [main] pull_request: branches: [main] jobs: tests: runs-on: ubuntu-latest strategy: matrix: features: - alloc - heapless - std - alloc,derive - heapless,derive - std,derive steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal - name: Test with a feature subset uses: actions-rs/cargo@v1 with: command: test args: --features ${{ matrix.features }} --verbose flexiber-0.1.0/.github/workflows/docs.yml000064400000000000000000000010410072674642500165160ustar 00000000000000name: Docs on: [push] jobs: docs: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal - name: Build Documentation run: cargo doc --features derive && scripts/make-toplevel-index.sh - name: Deploy Docs uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./target/doc flexiber-0.1.0/.gitignore000064400000000000000000000000230072674642500134350ustar 00000000000000/target Cargo.lock flexiber-0.1.0/Cargo.toml0000644000000024040000000000100106300ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "flexiber" version = "0.1.0" authors = ["Nicolas Stalder ", "RustCrypto Developers"] description = "Encoding and decoding of BER-TLV as described in ISO 7816-4, without allocations." readme = "README.md" keywords = ["crypto", "no_std", "serialization"] categories = ["cryptography", "data-structures", "encoding", "no-std"] license = "Apache-2.0 OR MIT" repository = "https://github.com/nickray/flexiber" [dependencies.delog] version = "0.1.0" [dependencies.flexiber_derive] version = "0.1" optional = true [dependencies.heapless] version = "0.7.0" optional = true [dev-dependencies.hex-literal] version = "0.3.1" [features] alloc = [] derive = ["flexiber_derive"] log-all = [] log-debug = [] log-error = [] log-info = [] log-none = [] log-warn = [] std = ["alloc"] flexiber-0.1.0/Cargo.toml.orig000064400000000000000000000014540072674642500143450ustar 00000000000000[package] name = "flexiber" version = "0.1.0" authors = ["Nicolas Stalder ", "RustCrypto Developers"] license = "Apache-2.0 OR MIT" edition = "2018" description = "Encoding and decoding of BER-TLV as described in ISO 7816-4, without allocations." repository = "https://github.com/nickray/flexiber" categories = ["cryptography", "data-structures", "encoding", "no-std"] keywords = ["crypto", "no_std", "serialization"] readme = "README.md" [dependencies] delog = "0.1.0" flexiber_derive = { version = "0.1", optional = true, path = "derive" } [dependencies.heapless] version = "0.7.0" optional = true [dev-dependencies] hex-literal = "0.3.1" [features] alloc = [] derive = ["flexiber_derive"] std = ["alloc"] log-all = [] log-none = [] log-info = [] log-debug = [] log-warn = [] log-error = [] flexiber-0.1.0/LICENSE-APACHE000064400000000000000000000240170072674642500134020ustar 00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed 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. flexiber-0.1.0/LICENSE-MIT000064400000000000000000000020430072674642500131050ustar 00000000000000Copyright (c) 2021 Nicolas Stalder Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. flexiber-0.1.0/README.md000064400000000000000000000012120072674642500127250ustar 00000000000000# flexiber Encoding and decoding of BER-TLV as described in ISO 7816-4, without allocations. Follows the approach taken in [`der`][der], and then in [`simple-tlv`][simple-tlv]. [der]: https://docs.rs/der [simple-tlv]: https://docs.rs/simple-tlv #### License `flexiber` is licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT License](LICENSE-MIT) at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. flexiber-0.1.0/bacon.toml000064400000000000000000000015010072674642500134260ustar 00000000000000 # This is a configuration file for the bacon tool # More info at https://github.com/Canop/bacon default_job = "check" [jobs] [jobs.check] # command = ["cargo", "check", "--color", "always", "--features", "clients-1"] command = ["cargo", "check", "--color", "always"] need_stdout = false [jobs.check-all] command = ["cargo", "check", "--tests", "--color", "always"] need_stdout = false [jobs.light] command = ["cargo", "check", "--color", "always"] need_stdout = false [jobs.clippy] command = ["cargo", "clippy", "--color", "always"] need_stdout = false [jobs.test] command = ["cargo", "test", "--color", "always"] need_stdout = true [jobs.test-derive] command = ["cargo", "test", "--features", "derive", "--color", "always"] need_stdout = true [jobs.doc] command = ["cargo", "doc", "--color", "always"] need_stdout = true flexiber-0.1.0/scripts/make-toplevel-index.sh000075500000000000000000000071450072674642500173610ustar 00000000000000#!/usr/bin/env bash # Copyright Materialize, Inc. All rights reserved. # # Use of this software is governed by the Business Source License # included in the LICENSE file at the root of this repository. # # As of the Change Date specified in that file, in accordance with # the Business Source License, use of this software will be governed # by the Apache License, Version 2.0. # # doc — renders API documentation. set -euo pipefail crate=$(basename $(pwd)) # Create a nice homepage for the docs. It's awful that we have to copy the # HTML template like this, but the upstream issue [0] that would resolve this is # now five years old and doesn't look close to resolution. # [0]: https://github.com/rust-lang/cargo/issues/739 cat > target/doc/index.html < $crate

$crate documentation

This is the home of $crate's internal API documentation.

EOF # Make the logo link to the nice homepage we just created. Otherwise it just # links to the root of whatever crate you happen to be looking at. cat >> target/doc/main.js < { /// Byte slice being decoded. /// /// In the event an error was previously encountered this will be set to /// `None` to prevent further decoding while in a bad state. bytes: Option<&'a [u8]>, /// Position within the decoded slice. position: Length, } impl<'a> Decoder<'a> { /// Create a new decoder for the given byte slice. pub fn new(bytes: &'a [u8]) -> Self { Self { bytes: Some(bytes), position: Length::zero(), } } /// Decode a value which impls the [`Decodable`] trait. pub fn decode>(&mut self) -> Result { if self.is_failed() { self.error(ErrorKind::Failed)?; } T::decode(self).map_err(|e| { self.bytes.take(); e.nested(self.position) }) } /// Decode a TaggedValue with tag checked to be as expected, returning the value pub fn decode_tagged_value + TagLike, V: Decodable<'a>>(&mut self, tag: T) -> Result { let tagged: crate::TaggedSlice = self.decode()?; tagged.tag().assert_eq(tag)?; Self::new(tagged.as_bytes()).decode() } /// Decode a TaggedSlice with tag checked to be as expected, returning the value pub fn decode_tagged_slice + TagLike>(&mut self, tag: T) -> Result<&'a [u8]> { let tagged: crate::TaggedSlice = self.decode()?; tagged.tag().assert_eq(tag)?; Ok(tagged.as_bytes()) } /// Return an error with the given [`ErrorKind`], annotating it with /// context about where the error occurred. pub fn error(&mut self, kind: ErrorKind) -> Result { self.bytes.take(); Err(kind.at(self.position)) } /// Did the decoding operation fail due to an error? pub fn is_failed(&self) -> bool { self.bytes.is_none() } /// Finish decoding, returning the given value if there is no /// remaining data, or an error otherwise pub fn finish(self, value: T) -> Result { if self.is_failed() { Err(ErrorKind::Failed.at(self.position)) } else if !self.is_finished() { Err(ErrorKind::TrailingData { decoded: self.position, remaining: self.remaining_len()?, } .at(self.position)) } else { Ok(value) } } /// Have we decoded all of the bytes in this [`Decoder`]? /// /// Returns `false` if we're not finished decoding or if a fatal error /// has occurred. pub fn is_finished(&self) -> bool { self.remaining().map(|rem| rem.is_empty()).unwrap_or(false) } /// Decode a single byte, updating the internal cursor. pub(crate) fn byte(&mut self) -> Result { match self.bytes(1u8)? { [byte] => Ok(*byte), _ => self.error(ErrorKind::Truncated), } } /// Obtain a slice of bytes of the given length from the current cursor /// position, or return an error if we have insufficient data. pub(crate) fn bytes(&mut self, len: impl TryInto) -> Result<&'a [u8]> { if self.is_failed() { self.error(ErrorKind::Failed)?; } let len = len .try_into() .or_else(|_| self.error(ErrorKind::Overflow))?; let result = self .remaining()? .get(..len.to_usize()) .ok_or(ErrorKind::Truncated)?; self.position = (self.position + len)?; Ok(result) } /// Peek at the next byte in the decoder without modifying the cursor. pub(crate) fn peek(&self) -> Option { self.remaining() .ok() .and_then(|bytes| bytes.get(0).cloned()) } /// Obtain the remaining bytes in this decoder from the current cursor /// position. fn remaining(&self) -> Result<&'a [u8]> { self.bytes .and_then(|b| b.get(self.position.into()..)) .ok_or_else(|| ErrorKind::Truncated.at(self.position)) } /// Get the number of bytes still remaining in the buffer. fn remaining_len(&self) -> Result { self.remaining()?.len().try_into() } } impl<'a> From<&'a [u8]> for Decoder<'a> { fn from(bytes: &'a [u8]) -> Decoder<'a> { Decoder::new(bytes) } } #[cfg(test)] mod tests { use crate::{Decodable, Tag, TaggedSlice}; #[test] fn zero_length() { let buf: &[u8] = &[0x05, 0x00]; let ts = TaggedSlice::from_bytes(buf).unwrap(); assert_eq!(ts, TaggedSlice::from(Tag::universal(0x5), &[]).unwrap()); } } // #[cfg(test)] // mod tests { // use super::Decoder; // use crate::{Decodable, ErrorKind, Length, Tag}; // #[test] // fn truncated_message() { // let mut decoder = Decoder::new(&[]); // let err = bool::decode(&mut decoder).err().unwrap(); // assert_eq!(ErrorKind::Truncated, err.kind()); // assert_eq!(Some(Length::zero()), err.position()); // } // #[test] // fn invalid_field_length() { // let mut decoder = Decoder::new(&[0x02, 0x01]); // let err = i8::decode(&mut decoder).err().unwrap(); // assert_eq!(ErrorKind::Length { tag: Tag::Integer }, err.kind()); // assert_eq!(Some(Length::from(2u8)), err.position()); // } // #[test] // fn trailing_data() { // let mut decoder = Decoder::new(&[0x02, 0x01, 0x2A, 0x00]); // let x = decoder.decode().unwrap(); // assert_eq!(42i8, x); // let err = decoder.finish(x).err().unwrap(); // assert_eq!( // ErrorKind::TrailingData { // decoded: 3u8.into(), // remaining: 1u8.into() // }, // err.kind() // ); // assert_eq!(Some(Length::from(3u8)), err.position()); // } // } flexiber-0.1.0/src/encoder.rs000064400000000000000000000140150072674642500142270ustar 00000000000000use core::convert::{TryFrom, TryInto}; use crate::{Encodable, ErrorKind, header::Header, Length, Result, Tag}; /// BER-TLV encoder. #[derive(Debug)] pub struct Encoder<'a> { /// Buffer into which BER-TLV-encoded message is written bytes: Option<&'a mut [u8]>, /// Total number of bytes written to buffer so far position: Length, } impl<'a> Encoder<'a> { /// Create a new encoder with the given byte slice as a backing buffer. pub fn new(bytes: &'a mut [u8]) -> Self { Self { bytes: Some(bytes), position: Length::zero(), } } /// Encode a value which impls the [`Encodable`] trait. pub fn encode(&mut self, encodable: &T) -> Result<()> { if self.is_failed() { self.error(ErrorKind::Failed)?; } encodable.encode(self).map_err(|e| { self.bytes.take(); e.nested(self.position) }) } /// Return an error with the given [`ErrorKind`], annotating it with /// context about where the error occurred. pub fn error(&mut self, kind: ErrorKind) -> Result { self.bytes.take(); Err(kind.at(self.position)) } /// Did the decoding operation fail due to an error? pub fn is_failed(&self) -> bool { self.bytes.is_none() } /// Finish encoding to the buffer, returning a slice containing the data /// written to the buffer. pub fn finish(self) -> Result<&'a [u8]> { let position = self.position; match self.bytes { Some(bytes) => bytes .get(..self.position.into()) .ok_or_else(|| ErrorKind::Truncated.at(position)), None => Err(ErrorKind::Failed.at(position)), } } /// Encode a collection of values which impl the [`Encodable`] trait under a given tag. pub fn encode_tagged_collection(&mut self, tag: Tag, encodables: &[&dyn Encodable]) -> Result<()> { let expected_len = Length::try_from(encodables)?; Header::new(tag, expected_len).and_then(|header| header.encode(self))?; let mut nested_encoder = Encoder::new(self.reserve(expected_len)?); for encodable in encodables { encodable.encode(&mut nested_encoder)?; } if nested_encoder.finish()?.len() == expected_len.into() { Ok(()) } else { self.error(ErrorKind::Length { tag }) } } /// Encode a collection of values which impl the [`Encodable`] trait under a given tag. pub fn encode_untagged_collection(&mut self, encodables: &[&dyn Encodable]) -> Result<()> { let expected_len = Length::try_from(encodables)?; let mut nested_encoder = Encoder::new(self.reserve(expected_len)?); for encodable in encodables { encodable.encode(&mut nested_encoder)?; } Ok(()) } /// Encode a single byte into the backing buffer. pub(crate) fn byte(&mut self, byte: u8) -> Result<()> { match self.reserve(1u8)?.first_mut() { Some(b) => { *b = byte; Ok(()) } None => self.error(ErrorKind::Truncated), } } /// Encode the provided byte slice into the backing buffer. pub(crate) fn bytes(&mut self, slice: &[u8]) -> Result<()> { self.reserve(slice.len())?.copy_from_slice(slice); Ok(()) } /// Reserve a portion of the internal buffer, updating the internal cursor /// position and returning a mutable slice. fn reserve(&mut self, len: impl TryInto) -> Result<&mut [u8]> { let len = len .try_into() .or_else(|_| self.error(ErrorKind::Overflow))?; if len > self.remaining_len()? { self.error(ErrorKind::Overlength)?; } let end = (self.position + len).or_else(|e| self.error(e.kind()))?; let range = self.position.into()..end.into(); let position = &mut self.position; // TODO(tarcieri): non-panicking version of this code // We ensure above that the buffer is untainted and there is sufficient // space to perform this slicing operation, however it would be nice to // have fully panic-free code. // // Unfortunately tainting the buffer on error is tricky to do when // potentially holding a reference to the buffer, and failure to taint // it would not uphold the invariant that any errors should taint it. let slice = &mut self.bytes.as_mut().expect("DER encoder tainted")[range]; *position = end; Ok(slice) } /// Get the size of the buffer in bytes. fn buffer_len(&self) -> Result { self.bytes .as_ref() .map(|bytes| bytes.len()) .ok_or_else(|| ErrorKind::Failed.at(self.position)) .and_then(TryInto::try_into) } /// Get the number of bytes still remaining in the buffer. fn remaining_len(&self) -> Result { self.buffer_len()? .to_usize() .checked_sub(self.position.into()) .ok_or_else(|| ErrorKind::Truncated.at(self.position)) .and_then(TryInto::try_into) } } #[cfg(test)] mod tests { use crate::{Encodable, Tag, TaggedSlice}; #[test] fn zero_length() { let tv = TaggedSlice::from(Tag::universal(5), &[]).unwrap(); let mut buf = [0u8; 4]; assert_eq!(tv.encode_to_slice(&mut buf).unwrap(), &[0x5, 0x00]); let tv = TaggedSlice::from(Tag::application(5).constructed(), &[]).unwrap(); let mut buf = [0u8; 4]; assert_eq!(tv.encode_to_slice(&mut buf).unwrap(), &[(0b01 << 6) | (1 << 5) | 5, 0x00]); } // use super::Encoder; // use crate::{ErrorKind, Length}; // #[test] // fn overlength_message() { // let mut buffer = []; // let mut encoder = Encoder::new(&mut buffer); // let err = false.encode(&mut encoder).err().unwrap(); // assert_eq!(err.kind(), ErrorKind::Overlength); // assert_eq!(err.position(), Some(Length::zero())); // } } flexiber-0.1.0/src/error.rs000064400000000000000000000150520072674642500137430ustar 00000000000000//! Error types. // pub use core::str::Utf8Error; use crate::{Length, Tag}; use core::{convert::Infallible, fmt}; /// Result type. pub type Result = core::result::Result; /// Error type. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Error { /// Kind of error kind: ErrorKind, /// Position inside of message where error occurred position: Option, } impl Error { /// Create a new [`Error`] pub fn new(kind: ErrorKind, position: Length) -> Error { Error { kind, position: Some(position), } } /// Get the [`ErrorKind`] which occurred. pub fn kind(self) -> ErrorKind { self.kind } /// Get the position inside of the message where the error occurred. pub fn position(self) -> Option { self.position } /// For errors occurring inside of a nested message, extend the position /// count by the location where the nested message occurs. pub fn nested(self, nested_position: Length) -> Self { // TODO(tarcieri): better handle length overflows occurring in this calculation? let position = (nested_position + self.position.unwrap_or_default()).ok(); Self { kind: self.kind, position, } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.kind)?; if let Some(pos) = self.position { write!(f, " at BER-TLV byte {}", pos)?; } Ok(()) } } impl From for Error { fn from(kind: ErrorKind) -> Error { Error { kind, position: None, } } } impl From for Error { fn from(_: Infallible) -> Error { unreachable!() } } // impl From for Error { // fn from(err: Utf8Error) -> Error { // Error { // kind: ErrorKind::Utf8(err), // position: None, // } // } // } // #[cfg(feature = "oid")] // impl From for Error { // fn from(_: const_oid::Error) -> Error { // ErrorKind::Oid.into() // } // } #[cfg(feature = "std")] impl std::error::Error for ErrorKind {} /// Error type. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub enum ErrorKind { /// Operation failed due to previous error Failed, /// Class has more than 2 bytes InvalidClass { value: u8 }, /// Invalid tag InvalidTag { /// Raw byte value of the tag byte: u8, }, /// Length greater than u16::MAX InvalidLength, /// Incorrect length for a given field Length { /// Tag type of the value being decoded tag: Tag, }, // /// Message is not canonically encoded // Noncanonical, // /// Malformed OID // Oid, /// Integer overflow occurred (library bug!) Overflow, /// Message is longer than BER-TLV's limits support Overlength, /// Undecoded trailing data at end of message TrailingData { /// Length of the decoded data decoded: Length, /// Total length of the remaining data left in the buffer remaining: Length, }, /// Unexpected end-of-message/nested field when decoding Truncated, /// Encoded message is shorter than the expected length /// (i.e. an `Encodable` impl on a particular type has a buggy `encoded_length`) Underlength { /// Expected length expected: Length, /// Actual length actual: Length, }, /// Unexpected tag UnexpectedTag { /// Tag the decoder was expecting (if there is a single such tag). /// /// `None` if multiple tags are expected/allowed, but the `actual` tag /// does not match any of them. expected: Option, /// Actual tag encountered in the message actual: Tag, }, // /// Unknown/unsupported tag // UnknownTag { // /// Raw byte value of the tag // byte: u8, // }, // /// UTF-8 errors // Utf8(Utf8Error), // /// Unexpected value // Value { // /// Tag of the unexpected value // tag: Tag, // }, /// Tag does not fit in 3 bytes UnsupportedTagSize, } impl ErrorKind { /// Annotate an [`ErrorKind`] with context about where it occurred, /// returning an error. pub fn at(self, position: Length) -> Error { Error::new(self, position) } } impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ErrorKind::Failed => write!(f, "operation failed"), ErrorKind::InvalidClass { value } => write!(f, "invalid class {}", value), ErrorKind::InvalidLength => write!(f, "length greater than protocol maximum"), ErrorKind::InvalidTag { byte } => { write!(f, "invalid BER-TLV tag: 0x{:02x}", byte) } ErrorKind::Length { tag } => write!(f, "incorrect length for {}", tag), // ErrorKind::Noncanonical => write!(f, "DER is not canonically encoded"), // ErrorKind::Oid => write!(f, "malformed OID"), ErrorKind::Overflow => write!(f, "integer overflow"), ErrorKind::Overlength => write!(f, "BER-TLV message is too long"), ErrorKind::TrailingData { decoded, remaining } => { write!( f, "trailing data at end of BER-TLV message: decoded {} bytes, {} bytes remaining", decoded, remaining ) } ErrorKind::Truncated => write!(f, "BER-TLV message is truncated"), ErrorKind::Underlength { expected, actual } => write!( f, "BER-TLV message too short: expected {}, got {}", expected, actual ), ErrorKind::UnexpectedTag { expected, actual } => { write!(f, "unexpected BER-TLV tag: ")?; if let Some(tag) = expected { write!(f, "expected {}, ", tag)?; } write!(f, "got {}", actual) } // ErrorKind::UnknownTag { byte } => { // write!(f, "unknown/unsupported ASN.1 DER tag: 0x{:02x}", byte) // } // ErrorKind::Utf8(e) => write!(f, "{}", e), // ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {}", tag), ErrorKind::UnsupportedTagSize => write!(f, "tags occupying more than 3 octets not supported"), } } } flexiber-0.1.0/src/header.rs000064400000000000000000000030460072674642500140420ustar 00000000000000//! BER-TLV headers. use crate::{Decodable, Decoder, Encodable, Encoder, ErrorKind, Length, Result, TagLike}; use core::convert::TryInto; /// BER-TLV headers: tag + length component of TLV-encoded values #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) struct Header { /// Tag representing the type of the encoded value pub tag: T, /// Length of the encoded value pub length: Length, } impl Header { /// Create a new [`Header`] from a [`Tag`] and a specified length. /// /// Returns [`Error`] if the length exceeds the limits of [`Length`] pub fn new(tag: T, length: impl TryInto) -> Result { let length = length.try_into().map_err(|_| ErrorKind::Overflow)?; Ok(Self { tag, length }) } } impl<'a, T> Decodable<'a> for Header where T: Decodable<'a> + TagLike, { fn decode<'b>(decoder: &'b mut Decoder<'a>) -> Result> { let tag = T::decode(decoder)?; let length = Length::decode(decoder).map_err(|e| { if e.kind() == ErrorKind::Overlength { ErrorKind::Length { tag: tag.embedding() }.into() } else { e } })?; Ok(Self { tag, length }) } } impl Encodable for Header where T: Encodable { fn encoded_length(&self) -> Result { self.tag.encoded_length()? + self.length.encoded_length()? } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { self.tag.encode(encoder)?; self.length.encode(encoder) } } flexiber-0.1.0/src/length.rs000064400000000000000000000136750072674642500141040ustar 00000000000000//! Length calculations for encoded ASN.1 DER values use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Result}; use core::{convert::TryFrom, fmt, ops::Add}; /// BER-TLV-encoded length. /// /// By definition, in the range `0..=65535` /// /// The length field consists of one or three consecutive bytes. /// - If the first byte is not `0xFF`, then the length field consists of a single byte encoding a number from /// zero to 254. /// - If the first byte is `0xFF`, then the length field consists of the subsequent two bytes interpreted as /// big-endian integer, with any value from zero to 65,535. #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] pub struct Length(pub(crate) u16); impl Length { /// Return a length of `0`. pub const fn zero() -> Self { Length(0) } /// Get the maximum length supported by BER-TLV: 65,535. pub const fn max() -> usize { u16::MAX as usize } /// Convert length to `usize`. pub fn to_usize(self) -> usize { self.0.into() } } /// Calculate the sum of the encoded lengths of the encodables. impl<'a> TryFrom<&'a [&'a dyn Encodable]> for Length { type Error = Error; fn try_from(encodables: &[&dyn Encodable]) -> Result { encodables .iter() .fold(Ok(Length::zero()), |sum, encodable| { sum + encodable.encoded_length()? }) } } impl Add for Length { type Output = Result; fn add(self, other: Self) -> Result { self.0 .checked_add(other.0) .map(Length) .ok_or_else(|| ErrorKind::Overflow.into()) } } impl Add for Length { type Output = Result; fn add(self, other: u8) -> Result { self + Length::from(other) } } impl Add for Length { type Output = Result; fn add(self, other: u16) -> Result { self + Length::from(other) } } impl Add for Length { type Output = Result; fn add(self, other: usize) -> Result { self + Length::try_from(other)? } } impl Add for Result { type Output = Self; fn add(self, other: Length) -> Self { self? + other } } impl From for Length { fn from(len: u8) -> Length { Length(len as u16) } } impl From for Length { fn from(len: u16) -> Length { Length(len) } } impl From for u16 { fn from(len: Length) -> u16 { len.0 } } impl From for usize { fn from(len: Length) -> usize { len.0 as usize } } impl TryFrom for Length { type Error = Error; fn try_from(len: usize) -> Result { u16::try_from(len) .map(Length) .map_err(|_| ErrorKind::Overflow.into()) } } impl Decodable<'_> for Length { fn decode(decoder: &mut Decoder<'_>) -> Result { match decoder.byte()? { len if len < 0x80 => Ok(len.into()), // we do not support indefinite lengths 0x80 => Err(ErrorKind::InvalidLength.into()), // one byte to follow 0x81 => { let len = decoder.byte()?; // allow non-minimum encodings Ok(len.into()) } 0x82 => { let len_hi = decoder.byte()? as u16; let len = (len_hi << 8) | (decoder.byte()? as u16); // allow non-minimum encodings Ok(len.into()) } _ => { // We specialize to a maximum 3-byte length encoding of length Err(ErrorKind::Overlength.into()) } } } } impl Encodable for Length { fn encoded_length(&self) -> Result { match self.0 { 0..=0x7F => Ok(Length(1)), 0x80..=0xFF => Ok(Length(2)), 0x100..=0xFFFF => Ok(Length(3)), } } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { match self.0 { 0..=0x7F => encoder.byte(self.0 as u8), 0x80..=0xFF => { encoder.byte(0x81)?; encoder.byte(self.0 as u8) } 0x100..=0xFFFF => { encoder.byte(0x82)?; encoder.byte((self.0 >> 8) as u8)?; encoder.byte((self.0 & 0xFF) as u8) } } } } impl fmt::Display for Length { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } #[cfg(test)] mod tests { use super::Length; use crate::{Decodable, Encodable}; #[test] fn decode() { assert_eq!(Length::zero(), Length::from_bytes(&[0x00]).unwrap()); assert_eq!(Length::from(0x7Fu8), Length::from_bytes(&[0x7F]).unwrap()); assert_eq!( Length::from(0x80u8), Length::from_bytes(&[0x81, 0x80]).unwrap() ); assert_eq!( Length::from(0xFFu8), Length::from_bytes(&[0x81, 0xFF]).unwrap() ); assert_eq!( Length::from(0x100u16), Length::from_bytes(&[0x82, 0x01, 0x00]).unwrap() ); } #[test] fn encode() { let mut buffer = [0u8; 3]; assert_eq!( &[0x00], Length::zero().encode_to_slice(&mut buffer).unwrap() ); assert_eq!( &[0x7F], Length::from(0x7Fu8).encode_to_slice(&mut buffer).unwrap() ); assert_eq!( &[0x81, 0x80], Length::from(0x80u8).encode_to_slice(&mut buffer).unwrap() ); assert_eq!( &[0x81, 0xFF], Length::from(0xFFu8).encode_to_slice(&mut buffer).unwrap() ); assert_eq!( &[0x82, 0x01, 0x00], Length::from(0x100u16).encode_to_slice(&mut buffer).unwrap() ); } #[test] fn reject_indefinite_lengths() { assert!(Length::from_bytes(&[0x80]).is_err()); } } flexiber-0.1.0/src/lib.rs000064400000000000000000000032030072674642500133530ustar 00000000000000//! # flexiber //! //! Implementation of the BER-TLV serialization format from ISO 7816-4:2005. //! //! ITU-T X.690 (08/2015) defines the BER, CER and DER encoding rules for ASN.1 //! //! The exact same document is [ISO/IET 8825-1][iso8825], which is freely available, //! inconveniently packed as a single PDF in a ZIP file :) //! //! ## Credits //! This library is a remix of `RustCrypto/utils/der`. //! //! The core idea taken from `der` is to have `Encodable` require an `encoded_length` method. //! By calling this recursively in a first pass, allocations required in other approaches are //! avoided. //! //! [iso8825]: https://standards.iso.org/ittf/PubliclyAvailableStandards/c068345_ISO_IEC_8825-1_2015.zip #![no_std] #![forbid(unsafe_code)] // #![warn(missing_docs, rust_2018_idioms)] #[cfg(feature = "alloc")] extern crate alloc; #[macro_use] extern crate delog; generate_macros!(); #[cfg(feature = "derive")] pub use flexiber_derive::{Decodable, Encodable}; #[cfg(feature = "std")] extern crate std; mod decoder; mod encoder; mod error; mod header; mod length; mod simpletag; mod slice; mod tag; mod tagged; mod traits; pub use decoder::Decoder; pub use encoder::Encoder; pub use error::{Error, ErrorKind, Result}; pub use length::Length; pub use simpletag::SimpleTag; pub use slice::Slice; pub use tag::{Class, Tag, TagLike}; pub use tagged::{TaggedSlice, TaggedValue}; pub use traits::{Container, Decodable, Encodable, Tagged}; #[cfg(feature = "heapless")] pub use traits::EncodableHeapless; // #[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)] // struct T2<'a> { // #[tlv(simple = "0x55", slice)] // a: &'a [u8], // } flexiber-0.1.0/src/nested.rs000064400000000000000000000052040072674642500140720ustar 00000000000000use crate::{Decoder, Encodable, Encoder, ErrorKind, Header, Length, Result, Slice, Tag, TaggedSlice}; /// Obtain the length of an ASN.1 `SEQUENCE` of [`Encodable`] values when /// serialized as ASN.1 DER, including the `SEQUENCE` tag and length prefix. pub fn encoded_length(/*tag: Tag,*/ encodables: &[&dyn Encodable]) -> Result { let inner_len = encoded_length_inner(encodables)?; // Header::new(tag, inner_len)?.encoded_length() + inner_len Header::new(crate::tag::MEANINGLESS_TAG, inner_len)?.encoded_length() + inner_len } /// Obtain the inner length of a container of [`Encodable`] values /// excluding the tag and length. pub(crate) fn encoded_length_inner(encodables: &[&dyn Encodable]) -> Result { encodables .iter() .fold(Ok(Length::zero()), |sum, encodable| { sum + encodable.encoded_length()? }) } /// Nested BER-TLV data objects. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Nested<'a> { /// Tag pub(crate) tag: Tag, /// Inner value pub(crate) slice: Slice<'a>, } impl<'a> Nested<'a> { /// Create a new [`Nested`] from a slice pub fn new(tag: Tag, slice: &'a [u8]) -> Result { Slice::new(slice) .map(|slice| Self { tag, slice }) .map_err(|_| ErrorKind::Length { tag }.into()) } /// Borrow the inner byte sequence pub fn as_bytes(&self) -> &'a [u8] { self.slice.as_bytes() } /// Get Tag pub fn tag(&self) -> Tag { self.tag } /// Decode nested values, creating a new [`Decoder`] for /// the data contained in the sequence's body and passing it to the provided /// [`FnOnce`]. pub fn decode_nested(&self, f: F) -> Result where F: FnOnce(&mut Decoder<'a>) -> Result, { let mut nested_decoder = Decoder::new(self.as_bytes()); let result = f(&mut nested_decoder)?; nested_decoder.finish(result) } } impl AsRef<[u8]> for Nested<'_> { fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl<'a> From> for Nested<'a> { fn from(tagged_slice: TaggedSlice<'a>) -> Nested<'a> { Self { tag: tagged_slice.tag(), slice: tagged_slice.value } } } impl<'a> From> for TaggedSlice<'a> { fn from(nested: Nested<'a>) -> TaggedSlice<'a> { TaggedSlice { tag: nested.tag(), value: nested.slice } } } impl<'a> Encodable for Nested<'a> { fn encoded_length(&self) -> Result { TaggedSlice::from(*self).encoded_length() } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { TaggedSlice::from(*self).encode(encoder) } } flexiber-0.1.0/src/simpletag.rs000064400000000000000000000041320072674642500145740ustar 00000000000000use core::convert::TryFrom; use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result, Tag, TagLike}; /// These are tags like in SIMPLE-TLV. /// /// The tag field consists of a single byte encoding a tag number from 1 to 254. The values '00' and 'FF' are invalid. /// /// The use case is that PIV (FIPS 201) data objects generally use BER-TLV, but, for historical reasons, /// label entries with "simple" tags (in particular, tag numbers larger than 30 are still encoded /// as single bytes. #[derive(Clone, Copy, Eq, PartialEq)] pub struct SimpleTag(u8); impl TryFrom for SimpleTag { type Error = Error; fn try_from(tag_number: u8) -> Result { match tag_number { byte if byte == 0 || byte == 0xFF => Err(ErrorKind::InvalidTag { byte }.into()), valid_tag_number => Ok(Self(valid_tag_number)), } } } impl TagLike for SimpleTag { fn embedding(self) -> Tag { use crate::Class::*; Tag { class: Universal, constructed: false, number: self.0 as u16 } } } impl Decodable<'_> for SimpleTag { fn decode(decoder: &mut Decoder<'_>) -> Result { decoder.byte().and_then(Self::try_from) } } impl Encodable for SimpleTag { fn encoded_length(&self) -> Result { Ok(1u8.into()) } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { encoder.byte(self.0) } } #[cfg(test)] mod tests { use core::convert::TryFrom; use crate::{Encodable, SimpleTag, TaggedSlice}; #[test] fn simple_tag() { let mut buf = [0u8; 384]; let tag = SimpleTag::try_from(37).unwrap(); let slice = &[1u8,2,3]; let short = TaggedSlice::from(tag, slice).unwrap(); assert_eq!( short.encode_to_slice(&mut buf).unwrap(), &[37, 0x3, 1, 2, 3] ); let slice = &[43u8; 256]; let long = TaggedSlice::from(tag, slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..4], &[37, 0x82, 0x01, 0x00]); assert_eq!(&encoded[4..], slice); } } flexiber-0.1.0/src/slice.rs000064400000000000000000000020060072674642500137040ustar 00000000000000use core::convert::TryFrom; use crate::{Length, Result}; /// Slice of at most `Length::max()` bytes. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Slice<'a> { /// Inner value inner: &'a [u8], /// Precomputed `Length` (avoids possible panicking conversions) length: Length, } impl<'a> Slice<'a> { /// Create a new [`Slice`], ensuring that the provided `slice` value /// is shorter than `Length::max()`. pub fn new(slice: &'a [u8]) -> Result { Ok(Self { inner: slice, length: Length::try_from(slice.len())?, }) } /// Borrow the inner byte slice pub fn as_bytes(&self) -> &'a [u8] { self.inner } /// Get the [`Length`] of this [`Slice`] pub fn length(self) -> Length { self.length } /// Is this [`Slice`] empty? pub fn is_empty(self) -> bool { self.length() == Length::zero() } } impl AsRef<[u8]> for Slice<'_> { fn as_ref(&self) -> &[u8] { self.as_bytes() } } flexiber-0.1.0/src/tag.rs000064400000000000000000000175370072674642500133770ustar 00000000000000use core::{convert::{TryFrom, TryInto}, fmt}; use crate::{Decodable, Decoder, Encodable, Encoder, Error, ErrorKind, Length, Result, TaggedValue}; const CLASS_OFFSET: usize = 6; const CONSTRUCTED_OFFSET: usize = 5; /// Indicator bit for constructed form encoding (i.e. vs primitive form) const CONSTRUCTED_FLAG: u8 = 1u8 << CONSTRUCTED_OFFSET; /// Indicator bit for constructed form encoding (i.e. vs primitive form) const NOT_LAST_TAG_OCTET_FLAG: u8 = 1u8 << 7; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u8)] /// Class of BER tag. pub enum Class { Universal = 0b00, Application = 0b01, Context = 0b10, Private = 0b11, } impl TryFrom for Class { type Error = Error; fn try_from(value: u8) -> Result { use Class::*; Ok(match value { 0b00 => Universal, 0b01 => Application, 0b10 => Context, 0b11 => Private, _ => return Err(ErrorKind::InvalidClass { value }.into()), }) } } /// The tag field consists of a single byte encoding a tag number from 1 to 254. The values '00' and 'FF' are invalid. #[derive(Clone, Copy, Eq, PartialEq)] pub struct Tag { pub class: Class, pub constructed: bool, pub number: u16, } impl Tag { pub const BOOLEAN: Self = Self::universal(0x1); pub const INTEGER: Self = Self::universal(0x1); pub const BIT_STRING: Self = Self::universal(0x3); pub const OCTET_STRING: Self = Self::universal(0x4); pub const NULL: Self = Self::universal(0x5); pub const OBJECT_IDENTIFIER: Self = Self::universal(0x6); pub const UTF8_STRING: Self = Self::universal(0xC); pub const PRINTABLE_STRING: Self = Self::universal(0x13); pub const UTC_TIME: Self = Self::universal(0x17); pub const GENERALIZED_TIME: Self = Self::universal(0x18); pub const SEQUENCE: Self = Self::universal(0x10).constructed(); pub const SET: Self = Self::universal(0x11).constructed(); pub fn from(class: Class, constructed: bool, number: u16) -> Self { Self { class, constructed, number } } pub const fn universal(number: u16) -> Self { Self { class: Class::Universal, constructed: false, number } } pub const fn application(number: u16) -> Self { Self { class: Class::Application, constructed: false, number } } pub const fn context(number: u16) -> Self { Self { class: Class::Context, constructed: false, number } } pub const fn private(number: u16) -> Self { Self { class: Class::Private, constructed: false, number } } pub const fn constructed(self) -> Self { let Self { class, constructed: _, number } = self; Self { class, constructed: true, number } } } impl TryFrom<&'_ [u8]> for Tag { type Error = Error; fn try_from(encoding: &[u8]) -> Result { let mut decoder = Decoder::new(encoding); decoder.decode() } } impl TryFrom for Tag { type Error = Error; fn try_from(encoded_value: u8) -> Result { [encoded_value].as_ref().try_into() } } /// This is the common trait that types to be used as tags /// are supposed to implement. pub trait TagLike: Copy + PartialEq + Sized { /// To stick with one Error type, make sure the tag type can somehow /// or other be coerced into a BerTag. fn embedding(self) -> Tag; /// Assert that this [`Tag`] matches the provided expected tag. /// /// On mismatch, returns an [`Error`] with [`ErrorKind::UnexpectedTag`]. fn assert_eq(self, expected: Self) -> Result { if self == expected { Ok(self) } else { Err(ErrorKind::UnexpectedTag { expected: Some(expected.embedding()), actual: self.embedding(), } .into()) } } /// Ergonomic way to get a TaggedValue for a given tag and value fn with_value(self, value: V) -> TaggedValue { TaggedValue::new(self, value) } } impl TagLike for Tag { fn embedding(self) -> Tag { self } } impl fmt::Display for Tag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // f.write_str(self.type_name()) // write!(f, "Tag('{:02x}')", self.0) core::fmt::Debug::fmt(self, f) } } impl fmt::Debug for Tag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut buf = [0u8; 3]; let mut encoder = Encoder::new(&mut buf); encoder.encode(self).unwrap(); write!(f, "Tag(class = {:?}, constructed = {}, number = {})", self.class, self.constructed, self.number) } } impl Encodable for Tag { fn encoded_length(&self) -> Result { match self.number { 0..=0x1E => Ok(Length(1)), 0x1F..=0x7F => Ok(Length(2)), 0x80..=0x3FFF => Ok(Length(3)), 0x4000..=0xFFFF => Ok(Length(4)), } } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { let first_byte = ((self.class as u8) << CLASS_OFFSET) | ((self.constructed as u8) << CONSTRUCTED_OFFSET); match self.number { 0..=0x1E => encoder.byte(first_byte | (self.number as u8)), 0x1F..=0x7F => { encoder.byte(first_byte | 0x1F)?; encoder.byte(self.number as u8) } 0x80..=0x3FFF => { encoder.byte(first_byte | 0x1F)?; encoder.byte(NOT_LAST_TAG_OCTET_FLAG | (self.number >> 7) as u8)?; encoder.byte((self.number & 0x7F) as u8) } 0x4000..=0xFFFF => { todo!(); } } } } impl Decodable<'_> for Tag { fn decode(decoder: &mut Decoder<'_>) -> Result { let first_byte = decoder.byte()?; let class = (first_byte >> 6).try_into()?; let constructed = first_byte & CONSTRUCTED_FLAG != 0; // remove class and primitive/constructed bits let first_byte_masked = first_byte & ((1 << 5) - 1); let number = match first_byte_masked { number @ 0..=0x1E => { number as u16 } _ => { let second_byte = decoder.byte()?; if second_byte & NOT_LAST_TAG_OCTET_FLAG == 0 { let number = second_byte; number as u16 } else { let number = second_byte & (!NOT_LAST_TAG_OCTET_FLAG); let third_byte = decoder.byte()?; if third_byte & NOT_LAST_TAG_OCTET_FLAG == 0 { ((number as u16) << 7) | (third_byte as u16) } else { todo!(); } } } }; Ok(Self { class, constructed, number }) } } #[cfg(test)] mod tests { use crate::{Decodable, Encodable, Tag}; #[test] fn reconstruct() { let mut buf = [0u8; 32]; let tag = Tag::universal(30); let encoded = tag.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x1E]); let tag2 = Tag::from_bytes(encoded).unwrap(); assert_eq!(tag, tag2); let tag = Tag::universal(31); let encoded = tag.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x1F, 0x1F]); let tag2 = Tag::from_bytes(encoded).unwrap(); assert_eq!(tag, tag2); let tag = Tag::universal(0xAA); let encoded = tag.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x1F, 0x81, 0x2A]); let tag2 = Tag::from_bytes(encoded).unwrap(); assert_eq!(tag, tag2); let tag = Tag::universal(0x10).constructed(); let encoded = tag.encode_to_slice(&mut buf).unwrap(); // assert_eq!(encoded, &[0x1F, 0x81, 0x2A]); assert_eq!(encoded, &[super::CONSTRUCTED_FLAG + 0x10]); let tag2 = Tag::from_bytes(encoded).unwrap(); assert_eq!(tag, tag2); } } flexiber-0.1.0/src/tagged.rs000064400000000000000000000147750072674642500140600ustar 00000000000000// //! Common handling for types backed by byte slices with enforcement of the // //! format-level length limitation of 65,535 bytes. use crate::{Decodable, Decoder, Encodable, Encoder, ErrorKind, header::Header, Length, Result, Slice, Tag, TagLike}; /// BER-TLV data object. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TaggedValue { tag: T, value: V, } /// Raw BER-TLV data object `TaggedValue>`. pub type TaggedSlice<'a, T=Tag> = TaggedValue, T>; impl TaggedValue where T: Copy, { pub fn new(tag: T, value: V) -> Self { Self { tag, value } } pub fn tag(&self) -> T { self.tag } } impl<'a, E, T> TaggedValue<&'a E, T> where E: Encodable, T: Copy + Encodable, { fn header(&self) -> Result> { Ok(Header { tag: self.tag(), length: self.value.encoded_length()?, }) } } impl<'a, E, T> Encodable for TaggedValue<&'a E, T> where E: Encodable, T: Copy + Encodable, { fn encoded_length(&self) -> Result { self.header()?.encoded_length()? + self.value.encoded_length()? } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { self.header()?.encode(encoder)?; encoder.encode(self.value) } } impl<'a, T> TaggedSlice<'a, T> where T: Copy { /// Create a new tagged slice, checking lengths. pub fn from(tag: T, slice: &'a [u8]) -> Result { Slice::new(slice) .map(|slice| Self { tag, value: slice }) .map_err(|_| (ErrorKind::InvalidLength).into()) } /// Borrow the inner byte slice. pub fn as_bytes(&self) -> &'a [u8] { self.value.as_bytes() } /// Get the length of the inner byte slice. pub fn length(&self) -> Length { self.value.length() } /// Is the inner byte slice empty? pub fn is_empty(&self) -> bool { self.value.is_empty() } /// Get the BER-TLV [`Header`] for this [`TaggedSlice`] value #[allow(clippy::unnecessary_wraps)] fn header(&self) -> Result> { Ok(Header { tag: self.tag(), length: self.length(), }) } /// Decode nested values, creating a new [`Decoder`] for /// the data contained in the sequence's body and passing it to the provided /// [`FnOnce`]. pub fn decode_nested(&self, f: F) -> Result where F: FnOnce(&mut Decoder<'a>) -> Result, { let mut nested_decoder = Decoder::new(self.as_bytes()); let result = f(&mut nested_decoder)?; nested_decoder.finish(result) } } impl<'a, T> Decodable<'a> for TaggedSlice<'a, T> where T: Decodable<'a> + TagLike, { fn decode(decoder: &mut Decoder<'a>) -> Result { let header = Header::::decode(decoder)?; let tag = header.tag; let len = header.length.to_usize(); let value = decoder.bytes(len).map_err(|_| ErrorKind::Length { tag: tag.embedding() })?; Self::from(tag, value) } } impl<'a, T> Encodable for TaggedSlice<'a, T> where T: Copy + Encodable { fn encoded_length(&self) -> Result { self.header()?.encoded_length()? + self.length() } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { self.header()?.encode(encoder)?; encoder.bytes(self.as_bytes()) } } // /// Obtain the length of an ASN.1 `SEQUENCE` of [`Encodable`] values when // /// serialized as ASN.1 DER, including the `SEQUENCE` tag and length prefix. // pub fn encoded_length2(/*tag: Tag,*/ encodables: &[&dyn Encodable]) -> Result { // let inner_len = Length::try_from(encodables)?; // Header::new(crate::tag::MEANINGLESS_TAG, inner_len)?.encoded_length() + inner_len // } // /// Obtain the inner length of a container of [`Encodable`] values // /// excluding the tag and length. // pub(crate) fn sum_encoded_lengths(encodables: &[&dyn Encodable]) -> Result { // encodables // .iter() // .fold(Ok(Length::zero()), |sum, encodable| { // sum + encodable.encoded_length()? // }) // } #[cfg(test)] mod tests { use core::convert::TryFrom; use crate::{Encodable, Tag, TaggedSlice}; #[test] fn encode() { let mut buf = [0u8; 1024]; let short = TaggedSlice::from(Tag::try_from(0x06).unwrap(), &[1, 2, 3]).unwrap(); assert_eq!( short.encode_to_slice(&mut buf).unwrap(), &[0x06, 0x3, 1, 2, 3] ); let slice = &[43u8; 256]; let long = TaggedSlice::from(Tag::universal(0x66), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0x1F, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::universal(0x66).constructed(), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0x3F, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::application(0x66), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0x5F, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::application(0x66).constructed(), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0x7F, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::context(0x66), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0x9F, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::context(0x66).constructed(), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0xBF, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::private(0x66), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0xDF, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); let long = TaggedSlice::from(Tag::private(0x66).constructed(), slice).unwrap(); let encoded = long.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], &[0xFF, 0x66, 0x82, 0x01, 0x00]); assert_eq!(&encoded[5..], slice); } } flexiber-0.1.0/src/traits.rs000064400000000000000000000462030072674642500141220ustar 00000000000000// pub use der::{Decodable, Encodable}; //! Trait definitions use core::convert::{TryFrom, TryInto}; use crate::{Decoder, Encoder, Error, header::Header, Length, Result, Tag, TaggedSlice, TaggedValue, TagLike}; #[cfg(feature = "alloc")] use { alloc::vec::Vec, core::iter, crate::ErrorKind, }; #[cfg(feature = "heapless")] use crate::ErrorKind; /// Decoding trait. /// /// Decode out of decoder, which essentially is a slice of bytes. /// /// One way to implement this trait is to implement `TryFrom, Error = Error>`. pub trait Decodable<'a>: Sized { /// Attempt to decode this message using the provided decoder. fn decode(decoder: &mut Decoder<'a>) -> Result; /// Parse `Self` from the provided byte slice. fn from_bytes(bytes: &'a [u8]) -> Result { let mut decoder = Decoder::new(bytes); let result = Self::decode(&mut decoder)?; decoder.finish(result) } } impl<'a, X> Decodable<'a> for X where X: TryFrom, Error = Error>, { fn decode(decoder: &mut Decoder<'a>) -> Result { TaggedSlice::decode(decoder) .and_then(Self::try_from) .or_else(|e| decoder.error(e.kind())) } } /// This implementation is quite gotcha-y, since only one byte is peeked. /// /// It should evaluate to the desired tag via `Tag::try_from(byte)?`. impl<'a, T> Decodable<'a> for Option where T: Decodable<'a> + Tagged, { fn decode(decoder: &mut Decoder<'a>) -> Result> { if let Some(byte) = decoder.peek() { debug_now!("comparing {} against {} interpreted as {}", &T::tag(), byte, Tag::try_from(byte)?); if T::tag() == Tag::try_from(byte)? { return T::decode(decoder).map(Some); } } Ok(None) } } /// Encoding trait. /// /// Encode into encoder, which essentially is a mutable slice of bytes. /// /// Additionally, the encoded length needs to be known without actually encoding. pub trait Encodable { /// Compute the length of this value in bytes when encoded as BER-TLV fn encoded_length(&self) -> Result; /// Encode this value as BER-TLV using the provided [`Encoder`]. fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()>; /// Encode this value to the provided byte slice, returning a sub-slice /// containing the encoded message. fn encode_to_slice<'a>(&self, buf: &'a mut [u8]) -> Result<&'a [u8]> { let mut encoder = Encoder::new(buf); self.encode(&mut encoder)?; encoder.finish() } /// Encode this message as BER-TLV, appending it to the provided /// byte vector. #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] fn encode_to_vec(&self, buf: &mut Vec) -> Result { let expected_len = self.encoded_length()?.to_usize(); let current_len = buf.len(); buf.reserve(expected_len); buf.extend(iter::repeat(0).take(expected_len)); // TODO(nickray): seems the original in `der` is incorrect here? // let mut encoder = Encoder::new(buf); let mut encoder = Encoder::new(&mut buf[current_len..]); self.encode(&mut encoder)?; let actual_len = encoder.finish()?.len(); if expected_len != actual_len { return Err(ErrorKind::Underlength { expected: expected_len.try_into()?, actual: actual_len.try_into()?, } .into()); } actual_len.try_into() } /// Serialize this message as a byte vector. #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] fn to_vec(&self) -> Result> { let mut buf = Vec::new(); self.encode_to_vec(&mut buf)?; Ok(buf) } } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] /// The equivalent of the `encode_to_vec` and `to_vec` methods. /// /// Separate trait because the generic parameter `N` would make `Encodable` not object safe. pub trait EncodableHeapless: Encodable { /// Encode this message as BER-TLV, appending it to the provided /// heapless byte vector. fn encode_to_heapless_vec(&self, buf: &mut heapless::Vec) -> Result { let expected_len = self.encoded_length()?.to_usize(); let current_len = buf.len(); // TODO(nickray): add a specific error for "Overcapacity" conditional on heapless feature? buf.resize_default(current_len + expected_len).map_err(|_| Error::from(ErrorKind::Overlength))?; let mut encoder = Encoder::new(&mut buf[current_len..]); self.encode(&mut encoder)?; let actual_len = encoder.finish()?.len(); if expected_len != actual_len { return Err(ErrorKind::Underlength { expected: expected_len.try_into()?, actual: actual_len.try_into()?, } .into()); } actual_len.try_into() } /// Serialize this message as a byte vector. fn to_heapless_vec(&self) -> Result> { let mut buf = heapless::Vec::new(); self.encode_to_heapless_vec(&mut buf)?; Ok(buf) } } #[cfg(feature = "heapless")] impl EncodableHeapless for X where X: Encodable {} /// Types that can be tagged. pub(crate) trait Taggable: Sized { fn tagged(&self, tag: T) -> TaggedValue<&Self, T> { TaggedValue::new(tag, self) } } impl Taggable for X where X: Sized, T: TagLike {} // /// Types with an associated BER-TLV [`Tag`]. // pub trait Tagged { // /// BER-TLV tag // const TAG: Tag; // } /// Types with an associated BER-TLV [`Tag`]. /// /// A tagged type implementing `Container` has a blanked implementation of `Encodable`. pub trait Tagged { /// The tag fn tag() -> Tag; } /// Multiple encodables in a container. /// /// A container implementing `Tagged` has a blanked implementation of `Encodable`. pub trait Container { /// Call the provided function with a slice of [`Encodable`] trait objects /// representing the fields of this message. /// /// This method uses a callback because structs with fields which aren't /// directly [`Encodable`] may need to construct temporary values from /// their fields prior to encoding. fn fields(&self, f: F) -> Result where F: FnOnce(&[&dyn Encodable]) -> Result; } impl Encodable for TaggedContainer where TaggedContainer: Tagged + Container { fn encoded_length(&self) -> Result { #[allow(clippy::redundant_closure)] // if we do as clippy tells, we get: // 183 | let value_length = self.fields(Length::try_from)?; // | ^^^^^^ one type is more general than the other // | // = note: expected type `FnOnce<(&[&dyn Encodable],)>` // found type `FnOnce<(&[&dyn Encodable],)>` let value_length = self.fields(|encodables| Length::try_from(encodables))?; Header::new(Self::tag(), value_length)?.encoded_length() + value_length } fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { self.fields(|fields| encoder.encode_tagged_collection(Self::tag(), fields)) } } ///// Multiple encodables, nested under a BER-TLV tag. ///// ///// This wraps up a common pattern for BER-TLV encoding. ///// Implementations obtain a blanket `Encodable` implementation //pub trait TaggedContainer: Container + Tagged {} //pub trait Untagged {} ///// Multiple encodables, side-by-side without a BER-TLV tag. ///// ///// This wraps up a common pattern for BER-TLV encoding. ///// Implementations obtain a blanket `Encodable` implementation //pub trait UntaggedContainer: Container + Untagged {} // impl Encodable for UC // where // UC: Untagged + Container, // { // fn encoded_length(&self) -> Result { // todo!(); // // let value_length = self.fields(|encodables| Length::try_from(encodables))?; // // Header::new(Self::tag(), value_length)?.encoded_length() + value_length // } // fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { // todo!(); // // self.fields(|fields| encoder.nested(Self::tag(), fields)) // } // } // pub type UntaggedContainer<'a> = &'a [&'a dyn Encodable]; // impl<'a> Encodable for UntaggedContainer<'a> { // fn encoded_length(&self) -> Result { // Length::try_from(*self) // } // fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { // for encodable in self.iter() { // encodable.encode(encoder)?; // } // Ok(()) // } // } impl Encodable for &[u8] { fn encoded_length(&self) -> Result { self.len().try_into() } /// Encode this value as BER-TLV using the provided [`Encoder`]. fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { encoder.bytes(self) } } // impl Encodable for Option<&[u8]> { // fn encoded_length(&self) -> Result { // match self { // Some(slice) => slice.encoded_length(), // None => Ok(Length::zero()), // } // } // /// Encode this value as BER-TLV using the provided [`Encoder`]. // fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { // match self { // Some(slice) => encoder.bytes(slice), // None => Ok(()) // } // } // } impl Encodable for Option where T: Encodable { fn encoded_length(&self) -> Result { match self { Some(t) => t.encoded_length(), None => Ok(Length::zero()), } } /// Encode this value as BER-TLV using the provided [`Encoder`]. fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { match self { Some(t) => t.encode(encoder), None => Ok(()) } } } macro_rules! impl_array { ($($N:literal),*) => { $( impl Encodable for [u8; $N] { fn encoded_length(&self) -> Result { Ok(($N as u8).into()) } /// Encode this value as BER-TLV using the provided [`Encoder`]. fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> { encoder.bytes(self.as_ref()) } } impl Decodable<'_> for [u8; $N] { fn decode(decoder: &mut Decoder<'_>) -> Result { use core::convert::TryInto; let bytes: &[u8] = decoder.bytes($N as u8)?; Ok(bytes.try_into().unwrap()) } } )* } } impl_array!( 0,1,2,3,4,5,6,7,8,9, 10,11,12,13,14,15,16,17,18,19, 20,21,22,23,24,25,26,27,28,29, 30,31,32 ); #[cfg(test)] mod tests { use core::convert::TryFrom; use crate::{Decodable, Encodable, Error, Result, Tag, TaggedSlice, TagLike}; use super::{Taggable, Tagged, Container}; // The types [u8; 2], [u8; 3], [u8; 4] stand in here for any types for the fields // of a struct that are Decodable + Encodable. This means they can decode to/encode from // a byte slice, but also that thye can declare their encoded length. // // The goal then is to tag the struct definition for a proc-macro that implements // nested BER-TLV objects (as this is what we need in PIV return values) // tag 0xAA #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct S { // tag 0x01 x: [u8; 2], // tag 0x02 y: [u8; 3], // tag 0x03 z: [u8; 4], } // this is what needs to be done to get `Decodable` impl<'a> TryFrom> for S { type Error = Error; fn try_from(tagged_slice: TaggedSlice<'a>) -> Result { tagged_slice.tag().assert_eq(Tag::try_from(0x0A).unwrap())?; tagged_slice.decode_nested(|decoder| { let x = decoder.decode_tagged_value(Tag::try_from(0x01).unwrap())?; let y = decoder.decode_tagged_value(Tag::try_from(0x02).unwrap())?; let z = decoder.decode_tagged_value(Tag::try_from(0x03).unwrap())?; Ok(Self { x, y, z }) }) } } // this is what needs to be done to get `Encodable` impl Tagged for S { fn tag() -> Tag { Tag::try_from(0x0A).unwrap() } } impl Container for S { fn fields(&self, field_encoder: F) -> Result where F: FnOnce(&[&dyn Encodable]) -> Result, { // both approaches equivalent field_encoder(&[ &(Tag::try_from(0x01).unwrap().with_value(&self.x.as_ref())), // &self.x.tagged(Tag::try_from(0x11).unwrap()), &self.y.as_ref().tagged(Tag::try_from(0x02).unwrap()), &self.z.as_ref().tagged(Tag::try_from(0x03).unwrap()), ]) } } #[test] fn reconstruct() { let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; let mut buf = [0u8; 1024]; let encoded = s.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x0A, 15, 0x01, 2, 1, 2, 0x02, 3, 3, 4, 5, 0x03, 4, 6, 7, 8, 9, ], ); let s2 = S::from_bytes(encoded).unwrap(); assert_eq!(s, s2); } // tag 0xBB #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct T { // tag 0x01 s: S, // tag 0x02 t: [u8; 3], } impl<'a> TryFrom> for T { type Error = Error; fn try_from(tagged_slice: TaggedSlice<'a>) -> Result { tagged_slice.tag().assert_eq(Tag::try_from(0x0B).unwrap())?; tagged_slice.decode_nested(|decoder| { let s = decoder.decode_tagged_value(Tag::try_from(0x01).unwrap())?; let t = decoder.decode_tagged_value(Tag::try_from(0x02).unwrap())?; Ok(Self { s, t }) }) } } impl Tagged for T { fn tag() -> Tag { Tag::try_from(0x0B).unwrap() } } impl Container for T { fn fields(&self, field_encoder: F) -> Result where F: FnOnce(&[&dyn Encodable]) -> Result, { field_encoder(&[ &self.s.tagged(Tag::try_from(0x1).unwrap()), &self.t.as_ref().tagged(Tag::try_from(0x2).unwrap()), ]) } } #[test] fn nesty() { let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; let t = T { s, t: [0xA, 0xB, 0xC] }; let mut buf = [0u8; 1024]; let encoded = t.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x0B, 24, 0x1, 17, 0x0A, 15, 0x01, 2, 1, 2, 0x02, 3, 3, 4, 5, 0x03, 4, 6, 7, 8, 9, 0x2, 3, 0xA, 0xB, 0xC ], ); let t2 = T::from_bytes(encoded).unwrap(); assert_eq!(t, t2); } // tag 0xCC #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct T2 { // no tag s: S, // tag 0x02 t: [u8; 3], } impl<'a> TryFrom> for T2 { type Error = Error; fn try_from(tagged_slice: TaggedSlice<'a>) -> Result { tagged_slice.tag().assert_eq(Tag::try_from(0x0C).unwrap())?; tagged_slice.decode_nested(|decoder| { let s = decoder.decode()?; let t = decoder.decode_tagged_value(Tag::try_from(0x02).unwrap())?; Ok(Self { s, t }) }) } } impl Tagged for T2 { fn tag() -> Tag { Tag::try_from(0x0C).unwrap() } } impl Container for T2 { fn fields(&self, field_encoder: F) -> Result where F: FnOnce(&[&dyn Encodable]) -> Result, { field_encoder(&[ &self.s, &self.t.as_ref().tagged(Tag::try_from(0x2).unwrap()), ]) } } #[test] fn nesty2() { let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; let t = T2 { s, t: [0xA, 0xB, 0xC] }; let mut buf = [0u8; 1024]; let encoded = t.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, // &[0xBB, 24, &[0x0C, 22, // 0x1, 17, 0x0A, 15, 0x01, 2, 1, 2, 0x02, 3, 3, 4, 5, 0x03, 4, 6, 7, 8, 9, 0x2, 3, 0xA, 0xB, 0xC ], ); let t2 = T2::from_bytes(encoded).unwrap(); assert_eq!(t, t2); } // no tag #[derive(Clone, Copy, Debug, Eq, PartialEq)] struct T3 { // no tag s: S, // tag 0x02 t: [u8; 3], } // impl<'a> TryFrom> for T2 { // type Error = Error; // fn try_from(tagged_slice: TaggedSlice<'a>) -> Result { // tagged_slice.tag().assert_eq(Tag::try_from(0xCC).unwrap())?; // tagged_slice.decode_nested(|decoder| { // let s = decoder.decode()?; // let t = decoder.decode_tag(Tag::try_from(0x02).unwrap())?; // Ok(Self { s, t }) // }) // } // } // impl TaggedContainer for T2 { // fn tag() -> Tag { // Tag::try_from(0xCC).unwrap() // } // fn fields(&self, field_encoder: F) -> Result // where // F: FnOnce(&[&dyn Encodable]) -> Result, // { // field_encoder(&[ // &self.s, // &self.t.tagged(Tag::try_from(0x2).unwrap()), // ]) // } // } // #[test] // fn nesty3() { // let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; // let t = T3 { s, t: [0xA, 0xB, 0xC] }; // let mut buf = [0u8; 1024]; // // let encoded = (&[ // // &t.s, // // &t.t.tagged(Tag::try_from(0x2).unwrap()), // // ]).encode_to_slice(&mut buf).unwrap(); // assert_eq!(encoded, // // &[0xBB, 24, // &[0xCC, 22, // // 0x1, 17, // 0xAA, 15, // 0x11, 2, 1, 2, // 0x22, 3, 3, 4, 5, // 0x33, 4, 6, 7, 8, 9, // 0x2, 3, // 0xA, 0xB, 0xC // ], // ); // let t2 = T2::from_bytes(encoded).unwrap(); // assert_eq!(t, t2); // } #[test] fn derive_option() { let mut buf = [0u8; 1024]; let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; let encoded = s.encode_to_slice(&mut buf).unwrap(); let mut decoder = crate::Decoder::new(&encoded); let s: Option = decoder.decode().unwrap(); assert!(s.is_some()); let empty = [0u8; 0]; let mut decoder = crate::Decoder::new(&empty); let s: Option = decoder.decode().unwrap(); assert!(s.is_none()); } } flexiber-0.1.0/tests/derive.rs000064400000000000000000000144220072674642500144430ustar 00000000000000//! Tests for custom derive support #![cfg(feature = "derive")] use flexiber::{Decodable, Encodable}; use flexiber as ber; #[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)] #[tlv(number = "0xAA")] struct S { #[tlv(slice, number = "0x11")] x: [u8; 2], #[tlv(slice, number = "0x22")] y: [u8; 3], #[tlv(slice, number = "0x33")] z: [u8; 4], } #[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)] #[tlv(application, number = "0xAA")] struct SApp { #[tlv(slice, number = "0x11")] x: [u8; 2], #[tlv(slice, number = "0x22")] y: [u8; 3], #[tlv(slice, number = "0x33")] z: [u8; 4], } #[test] fn derived_reconstruct() { let s = S { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; let mut buf = [0u8; 1024]; let encoded = s.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x1F, 0x81, 0x2A, 17, 0x11, 2, 1, 2, 0x1F, 0x22, 3, 3, 4, 5, 0x1F, 0x33, 4, 6, 7, 8, 9, ], ); let s2 = S::from_bytes(encoded).unwrap(); assert_eq!(s, s2); } #[test] fn derived_reconstruct_application() { let s = SApp { x: [1,2], y: [3,4,5], z: [6,7,8,9] }; let mut buf = [0u8; 1024]; let encoded = s.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, &[0x5F, 0x81, 0x2A, 17, 0x11, 2, 1, 2, 0x1F, 0x22, 3, 3, 4, 5, 0x1F, 0x33, 4, 6, 7, 8, 9, ], ); let s2 = SApp::from_bytes(encoded).unwrap(); assert_eq!(s, s2); } #[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)] #[tlv(constructed, number = "0x10")] struct T { #[tlv(number = "0x44", slice)] x: [u8; 1234], } #[test] fn pretty_big() { let mut x = [0u8; 1234]; for (i, x) in x.iter_mut().enumerate() { *x = i as _; }; let t = T { x }; let mut buf = [0u8; 1024]; assert!(t.encode_to_slice(&mut buf).is_err()); let mut buf = [0u8; 1500]; let encoded = t.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..9], [ // 1234 + 5 0x30, 0x82, 0x04, 0xD2 + 5, // 1234 0x1F, 0x44, 0x82, 0x04, 0xD2]); assert_eq!(&encoded[9..], x); let t2 = T::from_bytes(encoded).unwrap(); assert_eq!(t, t2); } #[derive(Clone, Copy, Debug, Decodable, Encodable, Eq, PartialEq)] struct T2 { #[tlv(private, primitive, number = "0x44", slice)] x: [u8; 1234], #[tlv(simple = "0x55", slice)] a: [u8; 5], } #[test] fn derive_untagged() { let mut x = [0u8; 1234]; for (i, x) in x.iter_mut().enumerate() { *x = i as _; }; let t = T2 { x, a: [17u8; 5] }; let mut buf = [0u8; 1500]; let encoded = t.encode_to_slice(&mut buf).unwrap(); assert_eq!(&encoded[..5], [ // 1234 223, 0x44, 0x82, 0x04, 0xD2]); assert_eq!(&encoded[5..(encoded.len() - 7)], x); assert_eq!(&encoded[(encoded.len() - 7)..], [0x55, 5, 17, 17, 17, 17, 17]); let t2 = T2::from_bytes(encoded).unwrap(); assert_eq!(t, t2); } #[derive(Clone, Copy)] pub struct PinUsagePolicy { piv_pin: bool, global_pin: bool, on_card_biometric_comparison: bool, has_virtual_contact_interface: bool, pairing_code_required_for_vci: Option, cardholder_prefers_global_pin: Option, } impl Default for PinUsagePolicy { fn default() -> Self { Self { piv_pin: true, global_pin: false, on_card_biometric_comparison: false, has_virtual_contact_interface: false, pairing_code_required_for_vci: None, cardholder_prefers_global_pin: None, } } } impl Decodable<'_> for PinUsagePolicy { fn decode(decoder: &mut ber::Decoder<'_>) -> ber::Result { let raw: [u8; 2] = decoder.decode()?; let capabilities = raw[0]; let has_global_pin = capabilities & (1 << 5) != 0; let has_virtual_contact_interface = capabilities & (1 << 3) != 0; Ok(Self { piv_pin: capabilities & (1 << 6) != 0, global_pin: has_global_pin, on_card_biometric_comparison: capabilities & (1 << 4) != 0, has_virtual_contact_interface, pairing_code_required_for_vci: if has_virtual_contact_interface { Some(capabilities & (1 << 2) != 0) } else { None }, cardholder_prefers_global_pin: if has_global_pin { Some(raw[1] == 0x20) } else { None }, }) } } impl Encodable for PinUsagePolicy { fn encoded_length(&self) -> ber::Result { Ok(2u8.into()) } fn encode(&self, encoder: &mut ber::Encoder<'_>) -> ber::Result<()> { let mut first_byte = 0u8; if self.piv_pin { first_byte |= 1 << 6; } if self.global_pin { first_byte |= 1 << 5; } if self.on_card_biometric_comparison { first_byte |= 1 << 4; } if self.has_virtual_contact_interface { first_byte |= 1 << 3; } if self.has_virtual_contact_interface && Some(true) == self.pairing_code_required_for_vci { first_byte |= 1 << 2; } let mut second_byte = 0u8; if self.global_pin { if let Some(prefers_global) = self.cardholder_prefers_global_pin { if prefers_global { second_byte = 0x20; } else { second_byte = 0x10; } } } encoder.encode(&[first_byte, second_byte]) } } #[derive(Decodable, Encodable)] #[tlv(application, constructed, number = "0x1E")] // = 0x7E pub struct DiscoveryObject { #[tlv(slice, application, number = "0xF")] piv_card_application_aid: [u8; 11], #[tlv(application, number = "0x2f")] pin_usage_policy: PinUsagePolicy, } impl Default for DiscoveryObject { fn default() -> Self { Self { piv_card_application_aid: hex_literal::hex!("A000000308 00001000 0100"), pin_usage_policy: Default::default(),//[0x40, 0x00], } } } #[test] fn discovery() { let disco = DiscoveryObject::default(); let mut buf = [0u8; 64]; let encoded = disco.encode_to_slice(&mut buf).unwrap(); assert_eq!(encoded, hex_literal::hex!("7e124f0ba0000003080000100001005f2f024000")); }