Decoding output a bit unintuitive #13

Open
opened 2020-04-01 09:43:14 -07:00 by ssadler · 7 comments
ssadler commented 2020-04-01 09:43:14 -07:00 (Migrated from github.com)

In the below script, the function test_encode prints:

a: [Explicit(ContextSpecific, 0, BigUint { data: [] }, Unknown(ContextSpecific, false, 2, BigUint { data: [] }, [0]))]
b: [Unknown(ContextSpecific, true, 0, BigUint { data: [] }, [128, 1, 0, 129, 1, 1])]

This is unexpected as the structure is the same in both occasions, and it appears to relate to this. I would expect the output to be as it is in b both times. I'm not exactly sure if the encoding is being done in the best way, but it conforms to the ASN model I am working with so I can't change it. For reference, the Haskell library asn1-encoding produces the following:

[Start (Container Context 0),Other Context 0 "\NUL",End (Container Context 0)]
[Start (Container Context 0),Other Context 0 "\NUL",Other Context 1 "\SOH",End (Container Context 0)]

Test script:

use num_traits::cast::{FromPrimitive};
use num_bigint::{BigInt, BigUint};
use simple_asn1::*;

#[test]
fn test_encode() {
    let a = encode_structure(0, &vec![vec![0]]);
    let b = encode_structure(0, &vec![vec![0], vec![1]]);
    println!("a: {:?}\nb: {:?}", from_der(&a).unwrap(), from_der(&b).unwrap());
}

fn encode_structure(type_id: u8, bufs: &[Vec<u8>]) -> Vec<u8> {
    let mut body = Vec::new();
    for (i, buf) in bufs.iter().enumerate() {
        let mut der = to_der(&ASN1Block::Unknown(ASN1Class::ContextSpecific, false, 0, BigUint::from_usize(i).unwrap(), buf.to_vec())).unwrap();
        body.append(&mut der);
    }
    to_der(&ASN1Block::Unknown(ASN1Class::ContextSpecific, true, 0, BigUint::from_u8(type_id).unwrap(), body)).unwrap()
}
In the below script, the function `test_encode` prints: ``` a: [Explicit(ContextSpecific, 0, BigUint { data: [] }, Unknown(ContextSpecific, false, 2, BigUint { data: [] }, [0]))] b: [Unknown(ContextSpecific, true, 0, BigUint { data: [] }, [128, 1, 0, 129, 1, 1])] ``` This is unexpected as the structure is the same in both occasions, and it appears to relate to [this](https://github.com/acw/simple_asn1/blob/47a5140bf29abf33e143bc6d53322eeb4979d686/src/lib.rs#L457). I would expect the output to be as it is in `b` both times. I'm not exactly sure if the encoding is being done in the best way, but it conforms to the ASN model I am working with so I can't change it. For reference, the Haskell library `asn1-encoding` produces the following: ```haskell [Start (Container Context 0),Other Context 0 "\NUL",End (Container Context 0)] [Start (Container Context 0),Other Context 0 "\NUL",Other Context 1 "\SOH",End (Container Context 0)] ``` Test script: ```rust use num_traits::cast::{FromPrimitive}; use num_bigint::{BigInt, BigUint}; use simple_asn1::*; #[test] fn test_encode() { let a = encode_structure(0, &vec![vec![0]]); let b = encode_structure(0, &vec![vec![0], vec![1]]); println!("a: {:?}\nb: {:?}", from_der(&a).unwrap(), from_der(&b).unwrap()); } fn encode_structure(type_id: u8, bufs: &[Vec<u8>]) -> Vec<u8> { let mut body = Vec::new(); for (i, buf) in bufs.iter().enumerate() { let mut der = to_der(&ASN1Block::Unknown(ASN1Class::ContextSpecific, false, 0, BigUint::from_usize(i).unwrap(), buf.to_vec())).unwrap(); body.append(&mut der); } to_der(&ASN1Block::Unknown(ASN1Class::ContextSpecific, true, 0, BigUint::from_u8(type_id).unwrap(), body)).unwrap() } ```
acw commented 2020-04-02 08:46:09 -07:00 (Migrated from github.com)

OK, interesting. Let me track back the reason for that particular if. Upon first examination, it does seem very suspicious.

Also, welcome fellow Haskell/Rust polyglot.

OK, interesting. Let me track back the reason for that particular `if`. Upon first examination, it does seem very suspicious. Also, welcome fellow Haskell/Rust polyglot.
Flakebi commented 2020-04-02 09:16:45 -07:00 (Migrated from github.com)

Seems like I introduced that code in #7.

I don’t remember why I wrote it that way, the goal was to parse OpenSSL EC keys, which contains explicitly tagged data. This comment might be the reason:

I am not sure how (and if at all) we can distinguish explicitly and
implicitly tagged data. For now they should be parsed as unknown (or
explicit if they are a valid ASN.1 block).

Seems like I introduced that code in #7. I don’t remember why I wrote it that way, the goal was to parse OpenSSL EC keys, which contains explicitly tagged data. This comment might be the reason: > I am not sure how (and if at all) we can distinguish explicitly and implicitly tagged data. For now they should be parsed as unknown (or explicit if they are a valid ASN.1 block).
acw commented 2020-04-02 09:31:15 -07:00 (Migrated from github.com)

Hmmmm! Maybe we can work together so we don't replicate each others work? Specifically:

@Flakebi: could you maybe commit an OpenSSL EC key or two in tests/, and maybe a test case that shows what you need the output to be?

@ssadler: could you commit this test case somewhere reasonable, with an assert for what you'd expect things to look like?

Maybe do this all in a fix/13 branch, since this will all be broken for a bit? Then we can all look for what the best solution is to meet both goals.

Hmmmm! Maybe we can work together so we don't replicate each others work? Specifically: @Flakebi: could you maybe commit an OpenSSL EC key or two in `tests/`, and maybe a test case that shows what you need the output to be? @ssadler: could you commit this test case somewhere reasonable, with an `assert` for what you'd expect things to look like? Maybe do this all in a `fix/13` branch, since this will all be broken for a bit? Then we can all look for what the best solution is to meet both goals.
ssadler commented 2020-04-02 10:03:06 -07:00 (Migrated from github.com)

I've made a branch here with the test: https://github.com/ssadler/simple_asn1/tree/fix/13

It passes without the the if constructed { block here. There's also a suggested fix for a warning but I don't know if it gives the right behavior.

I've made a branch here with the test: https://github.com/ssadler/simple_asn1/tree/fix/13 It passes without the the `if constructed {` block [here](https://github.com/ssadler/simple_asn1/blob/fix/13/src/lib.rs#L453). There's also a suggested fix for a warning but I don't know if it gives the right behavior.
Flakebi commented 2020-04-02 12:02:19 -07:00 (Migrated from github.com)

I added a small test with an explicit tag in #14.

I added a small test with an explicit tag in #14.
acw commented 2020-04-06 14:37:34 -07:00 (Migrated from github.com)

So, after doing some digging, I found this interesting quote:

If the type is tagged in EXPLICIT mode (or if the module includes the EXPLICIT TAGS clause in its header):
v [APPLICATION 0] EXPLICIT INTEGER ::= 38
the value is encoded in constructed form as a series of TLV triplets where the tag fields T contain all the subsequent tags until the UNIVERSAL class tag of the type is encountered;

(source, page 409)

Which makes me think that the current implementation is definitely wrong, but that the example @ssadler brings up might also have a not-entirely intuitive resolution. Anyone else find any interesting quotes on the topic of explicit tags?

So, after doing some digging, I found this interesting quote: > If the type is tagged in EXPLICIT mode (or if the module includes the EXPLICIT TAGS clause in its header): > `v [APPLICATION 0] EXPLICIT INTEGER ::= 38` > the value is encoded in constructed form as a series of TLV triplets where the tag fields T contain all the subsequent tags until the UNIVERSAL class tag of the type is encountered; ([source](http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF), page 409) Which makes me think that the current implementation is definitely wrong, but that the example @ssadler brings up might also have a not-entirely intuitive resolution. Anyone else find any interesting quotes on the topic of explicit tags?
ssadler commented 2020-04-06 19:11:35 -07:00 (Migrated from github.com)

I don't actually know how ASN works; my only thought is, was it tagged in Explicit mode? In this case there's no schema to indicate what mode it was tagged in, the parsing function just parses what it can according to what it knows.

The only other thing that occurrs to me, and again limited by my lack of knowledge of ASN, is that the parser could eagerly descend into the element and parse the children.

I don't actually know how ASN works; my only thought is, was it tagged in Explicit mode? In this case there's no schema to indicate what mode it was tagged in, the parsing function just parses what it can according to what it knows. The only other thing that occurrs to me, and again limited by my lack of knowledge of ASN, is that the parser could eagerly descend into the element and parse the children.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: acw/simple_asn1#13