Skip to content

Commit b099b1c

Browse files
committed
Use octet strings to decode bit strings.
1 parent 96c976b commit b099b1c

File tree

6 files changed

+76
-78
lines changed

6 files changed

+76
-78
lines changed

index.d.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface ASN1ContextTag {
2020
value: number
2121
contains: any
2222
}
23-
/** ASN1 JS bitstring. */
23+
/** ASN1 JS bit string. */
2424
export interface ASN1BitString {
2525
type: 'bitstring'
2626
value: Buffer
@@ -51,7 +51,11 @@ export function JStoASN1(
5151
| any[]
5252
| null,
5353
): ASN1Encoder
54-
/** Convert ASN1 BER encoded data to JS native types. */
54+
/**
55+
* Convert ASN1 BER encoded data to JS native types.
56+
* This supports number arrays, Buffer, ArrayBufferLike, base64 or hex
57+
* encded strings, or null input.
58+
*/
5559
export function ASN1toJS(
5660
data: string | null | number[] | Buffer | ArrayBuffer,
5761
):
@@ -68,15 +72,18 @@ export function ASN1toJS(
6872
| boolean
6973
| any[]
7074
| null
71-
/** Convert ASN1 BER encoded data to JS native types. */
75+
/**
76+
* Convert ASN1 BER encoded data to JS native types. This is the main decoder
77+
* class for decoding ASN1 encoded data.
78+
*/
7279
export class ASN1Decoder {
73-
/** Js constructor */
80+
/** JS constructor. */
7481
constructor(data: string | null | number[] | Buffer | ArrayBuffer)
7582
/** Create an instance of ANS1 from a buffer. */
7683
static fromBuffer(value: Buffer): ASN1Decoder
7784
/** Create an instance of ANS1 from Base64 encoded data. */
7885
static fromBase64(value: string): ASN1Decoder
79-
/** Create an instance of ANS1 from hex encoded data */
86+
/** Create an instance of ANS1 from hex encoded data. */
8087
static fromHex(value: string): ASN1Decoder
8188
/** Convert to an integer. */
8289
intoInteger(): number
@@ -103,6 +110,10 @@ export class ASN1Decoder {
103110
/** Convert a Sequence to an Array. */
104111
intoArray(): any[]
105112
}
113+
/**
114+
* Convert ASN1Data into ASN1 encoded data. This is the main encoder
115+
* class for encoding to ASN1 encoded data.
116+
*/
106117
export class ASN1Encoder {
107118
/** Create a new ASN1Encoder instance from any ASN1 encodable type. */
108119
constructor(
@@ -127,4 +138,9 @@ export class ASN1Encoder {
127138
/** Encode the ASN.1 data to a ASN.1 encoded base64 encoded string. */
128139
toBase64(): string
129140
}
141+
/**
142+
* ASN1 Iterator for sequences. Sequences use lazy loading iterators allowing
143+
* for chaining of operations while only executing on a consumer ensuring
144+
* O(n) operations.
145+
*/
130146
export class ASN1Iterator {}

src/asn1.rs

Lines changed: 14 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use napi::{
66
};
77
use num_bigint::BigInt;
88
use rasn::{
9-
ber::{de::Error as BERError, decode, encode},
10-
de::Needed,
9+
ber::{decode, encode},
1110
types::{Any, BitString, Class, OctetString, PrintableString},
1211
Decode, Tag,
1312
};
@@ -18,7 +17,7 @@ use crate::{
1817
ASN1BitString, ASN1Context, ASN1ContextTag, ASN1Object, ASN1RawBitString, ASN1Set, ASN1OID,
1918
},
2019
types::{ASN1Data, JsType},
21-
utils::{get_utc_date_time_from_asn1_milli, get_vec_from_js_unknown, repair_bit_string_data},
20+
utils::{get_utc_date_time_from_asn1_milli, get_vec_from_js_unknown},
2221
ASN1NAPIError,
2322
};
2423

@@ -76,18 +75,10 @@ impl ASN1Encoder {
7675
}
7776

7877
/// Encode ASN1Data to a Vec<u8> of ASN.1 encoded data.
79-
/// TODO Mising bits that the rasn library truncates.
8078
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
81-
if let Ok(mut data) = encode(&self.0) {
82-
match &self.0 {
83-
ASN1Data::Object(ASN1Object::BitString(val)) => {
84-
repair_bit_string_data(&mut data, val.clone().into_vec(), true);
85-
Ok(data)
86-
}
87-
_ => Ok(data),
88-
}
89-
} else {
90-
bail!(ASN1NAPIError::MalformedData)
79+
match encode(&self.0) {
80+
Ok(data) => Ok(data),
81+
Err(_) => bail!(ASN1NAPIError::InvalidDataEncoding),
9182
}
9283
}
9384

@@ -162,58 +153,32 @@ impl ASN1Decoder {
162153
/// Create an instance of ANS1 from Base64 encoded data.
163154
#[napi]
164155
pub fn from_base64(value: String) -> Result<ASN1Decoder> {
165-
if let Ok(result) = base64::decode(value) {
166-
Self::try_from(result.as_slice())
167-
} else {
168-
bail!(ASN1NAPIError::UnknownStringFormat)
156+
match base64::decode(value) {
157+
Ok(result) => Self::try_from(result.as_slice()),
158+
Err(_) => bail!(ASN1NAPIError::UnknownStringFormat),
169159
}
170160
}
171161

172162
/// Create an instance of ANS1 from hex encoded data.
173163
#[napi]
174164
pub fn from_hex(value: String) -> Result<ASN1Decoder> {
175-
if let Ok(result) = hex::decode(value) {
176-
Self::try_from(result.as_slice())
177-
} else {
178-
bail!(ASN1NAPIError::UnknownStringFormat)
165+
match hex::decode(value) {
166+
Ok(result) => Self::try_from(result.as_slice()),
167+
Err(_) => bail!(ASN1NAPIError::UnknownStringFormat),
179168
}
180169
}
181170

182171
/// Decode ASN1 encoded data.
183-
/// TODO Mising bits that the rasn library truncates.
184172
pub(crate) fn decode<T: Decode>(&self) -> Result<T> {
185173
match decode(&self.data) {
186174
Ok(data) => Ok(data),
187-
Err(err) => match err {
188-
BERError::Incomplete {
189-
needed: Needed::Size(val),
190-
} => {
191-
let data = [..val].iter().fold(self.data.clone(), |mut data, _| {
192-
data.push(0x00);
193-
data
194-
});
195-
if let Ok(result) = decode(&data) {
196-
Ok(result)
197-
} else {
198-
bail!(ASN1NAPIError::InvalidBitString)
199-
}
200-
}
201-
_ => bail!(ASN1NAPIError::MalformedData),
202-
},
175+
Err(_) => bail!(ASN1NAPIError::MalformedData),
203176
}
204177
}
205178

206179
/// Get a ASN1BitString object.
207180
pub(crate) fn get_raw_bit_string(&self) -> Result<ASN1RawBitString> {
208-
if let Ok(result) = self.decode::<ASN1RawBitString>() {
209-
let mut data = result.into_vec();
210-
211-
repair_bit_string_data(&mut data, self.data.clone(), false);
212-
213-
Ok(ASN1RawBitString::new(BitString::from_vec(data)))
214-
} else {
215-
bail!(ASN1NAPIError::InvalidBitString)
216-
}
181+
self.decode::<ASN1RawBitString>()
217182
}
218183

219184
/// Get a Context object.
@@ -227,12 +192,8 @@ impl ASN1Decoder {
227192
}
228193

229194
/// Decode an object to an ASN1Object.
230-
/// TODO Mising bits that the rasn library truncates.
231195
pub(crate) fn into_object(self) -> Result<ASN1Object> {
232-
match self.get_tag() {
233-
&Tag::BIT_STRING => Ok(ASN1Object::BitString(self.get_raw_bit_string()?)),
234-
_ => self.decode::<ASN1Object>(),
235-
}
196+
self.decode::<ASN1Object>()
236197
}
237198

238199
/// Convert to a big integer.

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ enum ASN1NAPIError {
5454
InvalidSimpleTypesOnly,
5555
#[error("Context data must be a sequence")]
5656
InvalidContextNonSequence,
57+
#[error("Could not encode provided data into ASN.1 format")]
58+
InvalidDataEncoding,
5759
}
5860

5961
/// Helper to convert a JS bigint to a JS Buffer

src/objects.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::VecDeque;
2+
13
use anyhow::{bail, Error, Result};
24
use napi::{Env, JsBuffer, JsNumber, JsObject, JsString, JsUnknown, ValueType};
35
use rasn::{
@@ -164,10 +166,16 @@ pub trait TypedObject<'a> {
164166

165167
impl ASN1RawBitString {
166168
pub fn new(mut value: BitString) -> Self {
169+
value.force_align();
167170
value.set_uninitialized(false);
171+
168172
Self(value)
169173
}
170174

175+
pub fn get_unused(&self) -> usize {
176+
self.0.trailing_zeros()
177+
}
178+
171179
pub fn into_vec(self) -> Vec<u8> {
172180
self.0.into_vec()
173181
}
@@ -231,16 +239,29 @@ type_object!(ASN1OID, "oid");
231239
type_object!(ASN1Set, "set");
232240
type_object!(ASN1ContextTag, "context");
233241

242+
/// TODO Mising bits that the rasn library truncates.
234243
impl Encode for ASN1RawBitString {
235244
fn encode_with_tag<E: Encoder>(&self, encoder: &mut E, tag: Tag) -> Result<(), E::Error> {
236-
encoder.encode_bit_string(tag, &self.0)?;
245+
let mut data = VecDeque::from(self.0.clone().into_vec());
246+
data.push_front(0x00);
247+
248+
encoder.encode_octet_string(tag, &Vec::from(data))?;
237249
Ok(())
238250
}
239251
}
240252

253+
/// TODO Mising bits that the rasn library truncates.
241254
impl Decode for ASN1RawBitString {
242255
fn decode_with_tag<D: Decoder>(decoder: &mut D, tag: Tag) -> Result<Self, D::Error> {
243-
Ok(ASN1RawBitString::new(decoder.decode_bit_string(tag)?))
256+
let mut data = VecDeque::from(decoder.decode_octet_string(tag)?);
257+
258+
if let Some(val) = data.get(0) {
259+
if val == &0x00 {
260+
data.pop_front();
261+
}
262+
}
263+
264+
Ok(ASN1RawBitString::new(BitString::from_vec(Vec::from(data))))
244265
}
245266
}
246267

src/utils.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,24 +134,6 @@ pub(crate) fn get_vec_from_js_unknown(data: JsUnknown) -> Result<Vec<u8>> {
134134
})
135135
}
136136

137-
/// Fixes discrepencies between asn1js and rasn regarding bit strings.
138-
/// TODO Mising bits that the rasn library truncates.
139-
pub(crate) fn repair_bit_string_data(data: &mut Vec<u8>, mut reference: Vec<u8>, incr_len: bool) {
140-
while let Some(val) = reference.pop() {
141-
if val == 0x00 {
142-
if let Some(pos) = data.get(1) {
143-
if incr_len {
144-
data[1] = pos + 1;
145-
}
146-
147-
data.push(0x00);
148-
}
149-
} else {
150-
break;
151-
}
152-
}
153-
}
154-
155137
#[cfg(test)]
156138
mod test {
157139
use chrono::{TimeZone, Utc};

tests/object.bitstring.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ const TEST_BITSTRINGS: lib.ASN1BitString[] = [
1717
'base64',
1818
),
1919
},
20+
{
21+
type: 'bitstring',
22+
value: Buffer.from(
23+
'MEQCIHd0xqmKNBV76Rqp65DJjZMoWsv6Z5THAtyNqkKeYX6pAiAwWqVPgu7W8WIQHOI6JIDsfve1xPskMUIgu56TFFqitA==',
24+
'base64',
25+
),
26+
},
2027
]
2128

2229
const TEST_BITSTRINGS_ASN1 = [
@@ -38,6 +45,15 @@ const TEST_BITSTRINGS_ASN1 = [
3845
0x65, 0xd4, 0x2c, 0xcf, 0xec, 0x2a, 0x6a, 0x30, 0xad, 0x47, 0xe1, 0x7e,
3946
0x00,
4047
]).buffer,
48+
new Uint8Array([
49+
0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x77, 0x74, 0xc6, 0xa9, 0x8a,
50+
0x34, 0x15, 0x7b, 0xe9, 0x1a, 0xa9, 0xeb, 0x90, 0xc9, 0x8d, 0x93, 0x28,
51+
0x5a, 0xcb, 0xfa, 0x67, 0x94, 0xc7, 0x02, 0xdc, 0x8d, 0xaa, 0x42, 0x9e,
52+
0x61, 0x7e, 0xa9, 0x02, 0x20, 0x30, 0x5a, 0xa5, 0x4f, 0x82, 0xee, 0xd6,
53+
0xf1, 0x62, 0x10, 0x1c, 0xe2, 0x3a, 0x24, 0x80, 0xec, 0x7e, 0xf7, 0xb5,
54+
0xc4, 0xfb, 0x24, 0x31, 0x42, 0x20, 0xbb, 0x9e, 0x93, 0x14, 0x5a, 0xa2,
55+
0xb4,
56+
]).buffer,
4157
]
4258

4359
test('JS ASN1BitString to ASN1 conversion', (t) => {

0 commit comments

Comments
 (0)