voltone / x509

Elixir package for working with X.509 certificates, Certificate Signing Requests (CSRs), Certificate Revocation Lists (CRLs) and RSA/ECC key pairs
BSD 3-Clause "New" or "Revised" License
120 stars 28 forks source link

way to retrieve a OCTET STRING from a sequence? #52

Open ferigis opened 2 years ago

ferigis commented 2 years ago

hello!

Thanks for this amazing library, it helped me a lot!

I am integrating with Apple Attest service, basically, one of the steps says this:

Obtain the value of the credCert extension with OID 1.2.840.113635.100.8.2, which is a DER-encoded ASN.1 sequence.

I think I got it doing this:

cred_cert |> Certificate.extensions() |> Extension.find({1, 2, 840, 113635, 100, 8, 2})

and that returns a tuple like this:

{:Extension, {1, 2, 840, 113635, 100, 8, 2}, false,  <<48, ...>>}

According to them, I have to "Decode the sequence and extract the single octet string that it contains".

I have been testing a lot but I have no idea how to decode that to the string they mention, do you know if there is a way to achieve that using your library?

Thanks!

voltone commented 2 years ago

The DER encoded octet string is the last element in the tuple (or rather, Extension record), starting with <<48, ...>>. You'd have to either generate a parser based on the ASN.1 syntax for that field, or in this case it may be simpler to build it by hand (it's supposed to be just a string inside a sequence). If you can send me an example of the full binary value I can probably tell you how you'd decode it with Elixir binary parsing.

ferigis commented 2 years ago

hi @voltone ! thanks for your time :)

This is a real example of the octet binary:

<<48, 36, 161, 34, 4, 32, 71, 220, 233, 192, 153, 72, 52, 227, 112, 240, 147,
  240, 245, 86, 159, 50, 3, 152, 97, 81, 206, 97, 45, 66, 15, 176, 144, 184, 34,
  97, 10, 171>>
voltone commented 2 years ago

Ok, so assuming the nonce length is always 32 bytes, and therefore none of the ASN.1 entities will every require multi-byte length encoding (which is needed for structures >127 bytes, IIRC), you should be able to do:

{:Extension, _oid, _critical, der} = Extension.find(extensions, {1, 2, 840, 113635, 100, 8, 2})
<<48, _outer_len::8, 161, len::8, seq::binary-size(len)>> = der
<<4, len::8, nonce::binary-size(len), _rest::binary>> = seq

Now you have the nonce value, and it will ignore any other values Apple may at some point add to that sequence. Again, as long as the overall structure doesn't get too big.

If you want a more robust parser you'd have to take the ASN.1 e.g. from here and use something like https://hex.pm/packages/asn1_compiler to generate a parser from it...

voltone commented 2 years ago

(If you don't care about robustness in case of changes on Apple's side at all, you could just do nonce = String.slice(ext_value, -32, 32), of course 😈)

ferigis commented 2 years ago

thanks a lot! you saved my day :)