Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoding Subject, public key info to be able to print #112

Open
greenpdx opened this issue Dec 11, 2022 · 18 comments
Open

Decoding Subject, public key info to be able to print #112

greenpdx opened this issue Dec 11, 2022 · 18 comments
Labels
area/standard Related to a ASN.1 standard implemented in rasn. good first issue Good for newcomers help wanted Extra attention is needed kind/enhancement New feature or request

Comments

@greenpdx
Copy link

I like rasn but I am have a problem decoding tbs_certificate fields. subject, subject_public_key_info, ...

When I print the subject I get
RdnSequence([{AttributeTypeAndValue { type: ObjectIdentifier([2, 5, 4, 3]), value: Any { contents: [12, 8, 84, 80, 32, 83, 83, 32, 67, 65] } }}, {AttributeTypeAndValue { type: ObjectIdentifier([2, 5, 4, 6]), value: Any { contents: [19, 2, 67, 82] } }}, {AttributeTypeAndValue { type: ObjectIdentifier([2, 5, 4, 7]), value: Any { contents: [12, 8, 80, 97, 108, 109, 97, 114, 101, 115] } }}])

CN = TP SS CA, C = CR, L = Palmares

and subject public key info.
SubjectPublicKeyInfo { algorithm: AlgorithmIdentifier { algorithm: ObjectIdentifier([1, 3, 101, 112]), parameters: None }, subject_public_key: BitVec<u8, bitvec::order::Msb0> { addr: 0x557c4f6c8a10, head: 000, bits: 256, capacity: 256 } [0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0] }

I would like to print out something like this
Public Key Algorithm: ED25519
ED25519 Public-Key:
pub:
4b:02:d2:8c:eb:99:32:e9:bb:92:80:83:1d:56:9e:
79:75:c7:e4:2f:4a:99:51:70:a9:41:94:ca:53:27:
73:6e

If you could show a code snippet that I could use to decode and print the cert.
specifically,
BitVec -> u8,
from OID ObjectIdentifier([1, 3, 101, 112]) to Ed25519
ObjectIdentifier([2, 5, 4, 3]), -> CN
value: Any { contents: [12, 8, 84, 80, 32, 83, 83, 32, 67, 65] } } -> TP SS CA

I hope to make a PR that would add these features

@XAMPPRocky
Copy link
Collaborator

Thank you for your issue! I would definitely like to improve both the debug printing and the API for these types. Here's how I think this could be added to the project.

  • Add manual Debug implementation for OID/ObjectIdentifier.
    • This should output both the raw identifier or the human readable version (if possible).
      • The raw identifer should be in { 2 999 } format.
      • The human readable version should be {joint-iso-itu-t(2) example(999)}.
  • Add a manual Debug implementation to AlgorithimIdentifier.
  • Add a manual Debug implementation to SubjectPublicKeyInfo.

We could also change the AlgorithimIdentifier to an enum of known and unknown variants, so you'd need one less step to decode it.

enum AlgorithimIdentifier {
    Ed25519,
    Ecdsa(EcpkParameters),
    Unknown {
        algorithm: ObjectIdentifier,
        parameters: Option<Any>,
    }
}

Then you'd add a manual decode and encode implementation that matches it into the original representation.

@XAMPPRocky XAMPPRocky added kind/enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers area/standard Related to a ASN.1 standard implemented in rasn. labels Dec 12, 2022
@greenpdx
Copy link
Author

Thanks for the quick answer.
I have specific questions:
What function do I use to convert a BitVec to Vec?
How do I look up an OID and/or add a OID to lookup?
what function do I use to convert/parse a RdnSequence to Vec
A value has a type, length, data, how do you lookup the value type?

I have been looking at x509_parser for ideas, but rasn is abstracted more.

@XAMPPRocky
Copy link
Collaborator

What function do I use to convert a BitVec to Vec?

If you mean just to get a slice it's bitvec.as_raw_slice(), but you don't want to do that generically, you should want to convert it to a human readable format in the Debug implementation.

How do I look up an OID and/or add a OID to lookup?

This currently does not exist, but you can see we do have table of OIDs already in the source code. So what's required is modifying this macro definition to generate a lookup table.

https://github.com/XAMPPRocky/rasn/blob/9fed0d7e61a9ecbd9f073eb365c8d85dda035404/src/types/oid.rs#L245-L264

what function do I use to convert/parse a RdnSequence to Vec

You should be calling RdnSequence's Debug implementation.

A value has a type, length, data, how do you lookup the value type?

You can't rely on the tag statically as a type can override the static tag (for example; fields or newtypes). You want to match by OID.

@greenpdx
Copy link
Author

I am working on creating a search for OIDs, The idea that I am working on is creating a constant array of ConstOid and name
const tbl: [(str, ConstOid)] = [($name, ConstOid), ($name, ConstOid), .... ];
I get errors of course.
I can't make pub const $name: ... and create a table, I would have to wrap the oids macro to create the table and that would hide the const need by rasn.
any ideas on how to proceed?

@XAMPPRocky
Copy link
Collaborator

any ideas on how to proceed?

You’re going to need to move this macro from being declarative, to being a procedural macro in rasn-macros to create the structure you want at compile time.

@greenpdx
Copy link
Author

I have written a Cert Request decode and encode but there is a problem with the public key in CSR.
here is the decode and the openssl output.

https://webencrypt.org/asn1js/
SEQUENCE (3 elem)
SEQUENCE (3 elem)
INTEGER 0
SEQUENCE (1 elem)
SET (1 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
IA5String Ed25519 Test
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
NULL
BIT STRING (256 bit) 0101111101110010110011111010011001011011101010010000110001000110000111…
SEQUENCE (2 elem)
OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
NULL
BIT STRING (512 bit) 0011011100100101110110011000001100101010110010111001010100010011110111…

here is the openssl output

openssl req -in tst.csr -text
Certificate Request:
Data:
Version: 1 (0x0)
Subject: CN = Ed25519 Test
Subject Public Key Info:
Public Key Algorithm: ED25519
Unable to load Public Key
40F72525827F0000:error:03000072:digital envelope routines:X509_PUBKEY_get0:decode error:../crypto/x509/x_pubkey.c:458:
Attributes:
Requested Extensions:
Signature Algorithm: ED25519
Signature Value:
37:25:d9:83:2a:cb:95:13:de:e1:ff:b2:d0:94:af:6b:a1:6f:
35:bc:46:55:eb:45:83:44:a6:37:45:ef:cc:69:90:9c:85:4f:
ef:17:53:64:53:fa:db:7a:72:f1:e9:85:bf:44:3b:08:d0:60:
df:d8:74:e7:35:35:9d:ed:c8:0d
-----BEGIN CERTIFICATE REQUEST-----
MIGYMEoCAQAwFzEVMBMGA1UEAxYMRWQyNTUxOSBUZXN0MCwwBwYDK2VwBQADIQBf
cs+mW6kMRhwQziU4+Zt4F+PoHCdI24Vce3LlTpwbEjAHBgMrZXAFAANBADcl2YMq
y5UT3uH/stCUr2uhbzW8RlXrRYNEpjdF78xpkJyFT+8XU2RT+tt6cvHphb9EOwjQ
YN/YdOc1NZ3tyA0=
-----END CERTIFICATE REQUEST-----

@XAMPPRocky
Copy link
Collaborator

I have written a Cert Request decode and encode but there is a problem with the public key in CSR.

Could you provide a bit more detail, what's the problem specifically?

@greenpdx
Copy link
Author

greenpdx commented Jan 13, 2023

I have written a Certificate signing request generation and decode. It works self contained, but I want to verify it using openssl. openssl ed25519 public key does not have any parameters so it expects the key material directly after the key_alg.
In rasn and x509-certificate libraries uses a NULL in the parameter field because it is an option.

I have placed a bug report for openssl
Ed25519 pubkey decode fails because parameters value is NULL openssl/openssl#20045

But I was wondering if rasn could not include a None option?

@XAMPPRocky
Copy link
Collaborator

In rasn and x509-certificate libraries uses a NULL in the parameter field because it is an option.

Hmm, that doesn't seem right. In ASN.1 you don't set NULL when an Option is None, you don't set anything. Can you try encoding it with the per branch of the library and see what happens?

@greenpdx
Copy link
Author

same. But I switch from Ia5String to Utf8String because Ia5String was giving me conversion issues

@greenpdx
Copy link
Author

use argparse::{ArgumentParser, StoreTrue, Store, StoreOption};
use std::io;
use std::io::prelude::;
use std::fs::File;
use std::process::exit;
use pem::{parse, Pem};
//use crate::error::Error;
extern crate alloc;
use rasn::prelude::
;
use rasn::{types::, Decode, Encode, der::{decode, encode}};
use rasn_pkix::
;

use ring::{
rand,
signature::{self, KeyPair, Ed25519KeyPair},
};
use std::ops::Deref;
use bitvec::prelude::*;

#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rasn(delegate)]
pub struct Version(u64);

impl Version {
pub const V1: Self = Self(0);
pub const V2: Self = Self(1);

/// Returns the raw value of the version. Note that the version is
/// zero-indexed (v1 is 0, v2 is 1, etc).
pub fn raw_value(self) -> u64 {
    self.0
}

}

impl Default for Version {
fn default() -> Self {
Self::V1
}
}

impl core::fmt::Display for Version {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(f, "{}", self.0.saturating_add(1))
}
}

/// Attributes.
///
/// asn.1 /// Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } ///

#[derive(AsnType, Clone, Debug, Decode, Encode, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Attributes(Vec);

#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CertificateRequestInfo {
//#[rasn(tag(explicit(0)), default)]
pub version: Version,
pub subject: Name,
pub subject_public_key_info: SubjectPublicKeyInfo,
#[rasn(tag(0))]
pub attributes: Option
}

#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CertificateRequest {
pub certification_request_info: CertificateRequestInfo,
pub signature_algorithm: AlgorithmIdentifier,
pub signature_value: BitString
}
/*
#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rasn(choice)]
pub enum Name {
RdnSequence(RdnSequence),
}
*/
fn rd_file(fname: String) -> Vec {
println!("{:?}", fname);
let mut f = File::open(fname).expect("Can't open file");
let mut data = Vec::new();
f.read_to_end(&mut data).expect("Failed to read file");

// read pem to der
//let  (pem, sz) = Pem::read(&mut buffer).expect("Can'tread file");
//pem
data

}

fn rdprivkey(file: String) -> Ed25519KeyPair {
// pkcs8 = InMemorySigningKeyPair::from_pkcs8_pem(rd_file(file)).expect("can't read pkcs8pem");
// println!("{:?}", pkcs8.verification_algorithm());
let byts = parse(rd_file(file)).expect("Read or der failed"); //private key
println!("{:?} {:?}", byts.tag, byts.contents);
let kp25519 = signature::Ed25519KeyPair::from_pkcs8_maybe_unchecked(&byts.contents).expect("Can't read key");
//let pubkey = kp25519.public_key().clone();
//println!("{:?}", &pubkey);

println!("{:?}", kp25519);
println!("{:?}", kp25519.public_key());
kp25519

}

pub fn mkAttribute(oidvec: Vec, val: &Utf8String) -> AttributeTypeAndValue {
let oid = ObjectIdentifier::new_unchecked((oidvec).into());
let cn = AttributeTypeAndValue {
r#type: oid,
//r#type: ObjectIdentifier::new_unchecked((&[2,5,4,3][..]).into()),
value: Any::new(
rasn::der::encode(&Utf8String::from(
val
)).unwrap()
)
};
cn
}

pub fn mkName(names: &Vec<(Vec,Utf8String)>) {

}

// CertificateRequestInfo
pub fn bld_cri(name: &Vec, attrs: &Vec, kp: &Ed25519KeyPair ) -> CertificateRequestInfo {

println!("{:?}", kp);
let version: Version = Version::V1;
let tst = mkAttribute(vec!(2,5,4,3), &attrs[0]);
let cn = AttributeTypeAndValue {
    r#type: ObjectIdentifier::new_unchecked((&[2,5,4,3][..]).into()),
    value: Any::new(
        rasn::der::encode(&Utf8String::from(
            "Ed25519"
        )).unwrap())
    };

println!("{:?} {:?}", cn, tst);
let par = vec!((vec!(2,5,4,3), Utf8String::from("test")), (vec!(2,5,4,4), Utf8String::from("other")));
let tst1 = mkName(&par);
let subject = Name::RdnSequence(vec![
    {
        let mut set = rasn::types::SetOf::new();
        set.insert(cn);
        set
    }
]);

println!("{:?}", subject);

let subject_public_key_info = SubjectPublicKeyInfo {
    algorithm: AlgorithmIdentifier {
        algorithm: ObjectIdentifier::new_unchecked(
            (&[1,3,101,112][..]).into(),
        ),
        parameters: Some(Any::new(rasn::der::encode(&()).unwrap())),
    },
    subject_public_key: BitString::from_slice(
        kp.public_key().as_ref().deref()
    ),
};
let attrs: Option<Attributes> = None;

let cri = CertificateRequestInfo {
    version: version,
    subject: subject,
    subject_public_key_info: subject_public_key_info,
    attributes: None,
};
cri
//println!("{:?}", cri);
//let cder = encode(&cri);
//println!("{:?}", cder);

}

fn main() {
println!("Hello, world!");
let mut verbose = false;
let mut file = "".to_string();
let mut privkey: Option = std::option::Option::None;
//let args =vec!();
{
let mut ap = ArgumentParser::new();
ap.set_description("Greet somebody.");
ap.stop_on_first_argument(true);
ap.refer(&mut verbose)
.add_option(&["-v", "--verbose"], StoreTrue,
"Be verbose");
ap.refer(&mut privkey) // sign private key .key
.add_option(&["--key"], StoreOption,
"Ket set");
ap.refer(&mut file) // certificate .crt
.add_argument("file", Store, "File name");
match ap.parse_args() {
Ok(()) => { }
Err(e) => {
exit(e);
}
}
}
//let crtname = file.clone() + &".crt".to_string();
let keyname = file.clone() + &".crt".to_string();
let mut pkcs8 = "".to_string();
match privkey {
Some(key) => {
pkcs8 = key.clone();
},
None => {
pkcs8 = file.clone() + &".pkcs8".to_string();
}
}
let signkey = rdprivkey(pkcs8);

//let crtdata = rd_file(crtname);
//let crtder = parse(crtdata).expect("cant pem to der");
//let crt: Certificate = rasn::der::decode(&crtder.contents).expect("no decode");
//println!("{:?}", crt);
let csrname = file.clone() + &".csr".to_string();
let csrdata = rd_file(csrname);
let csrder = parse(csrdata).expect("cant pem to der");
//println!("{:?}", csrder);
let csr: CertificateRequest = rasn::der::decode(&csrder.contents).expect("no decode");
//println!("{:?}", csr);

let cristruct = bld_cri(&vec!(Utf8String::from("test")), &vec!(Utf8String::from("bob")), &signkey);
let crider = encode(&cristruct).unwrap();
//let csr1: CertificateRequestInfo = rasn::der::decode(&crider).expect("no decode");
//println!("{:?} {:?} {:?}", &cristruct, &crider, csr1);

let sig = signkey.sign(&crider);
let alg = AlgorithmIdentifier {
    algorithm: ObjectIdentifier::new_unchecked(
        (&[1,3,101,112][..]).into(),
    ),
    parameters: Some(Any::new(rasn::der::encode(&()).unwrap())),
};
let csr = CertificateRequest {
    certification_request_info: cristruct,
    signature_algorithm: alg,
    signature_value: BitString::from_slice(sig.as_ref()),
};
let csrder = encode(&csr).unwrap();
println!("{:?}", &csr);
let peer_public_key_bytes = signkey.public_key().as_ref();
let peer_public_key =
    signature::UnparsedPublicKey::new(&signature::ED25519, peer_public_key_bytes);
peer_public_key.verify(&crider, sig.as_ref()).expect("Verify failed");

let pem = Pem {
    tag:String::from("CERTIFICATE REQUEST"),
    contents: csrder
};
println!("{:?}", pem::encode(&pem));

}

@greenpdx
Copy link
Author

It looks like if it is none it should just return

/// Encode the absent value of an optional field.
fn encode_none<E: Encode>(&mut self) -> Result<Self::Ok, Self::Error>;

/// Encode the absent value with `tag` of an optional field.
fn encode_none_with_tag(&mut self, tag: Tag) -> Result<Self::Ok, Self::Error>;

@greenpdx
Copy link
Author

I found problem with ed25519 parameters. my fault
I am using pkcs8 crate for reading in keys.

@XAMPPRocky
Copy link
Collaborator

@greenpdx Can I ask if there was a reason you were using pkcs8? Is there functionality in that crate that you'd like to see in the rasn PKIX crate?

@greenpdx
Copy link
Author

greenpdx commented Jan 17, 2023 via email

@greenpdx
Copy link
Author

I have created a PKCS8 reader and debug display.
But when I do it for ES256 and RSA the pub and key material are ASN1 sequences

How do I create a "switch" for the three different OIDs?
Is there a ASN1 raw decoder?

#[derive(AsnType, Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rasn(delegate)]
pub struct Version(u64);

impl Version {
pub const V1: Self = Self(0);
pub const V2: Self = Self(1);

/// Returns the raw value of the version. Note that the version is
/// zero-indexed (v1 is 0, v2 is 1, etc).
pub fn raw_value(self) -> u64 {
    self.0
}

}

impl Default for Version {
fn default() -> Self {
Self::V1
}
}

impl core::fmt::Display for Version {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(f, "{}", self.0.saturating_add(1))
}
}

#[derive(AsnType, Clone, Debug, Decode, Encode, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct PKCS8 {
//#[rasn(tag(explicit(0)), default)]
pub version: Version,
pub algorithm: AlgorithmIdentifier,
pub private_key: OctetString,
#[rasn(tag(0))]
pub attributes: Option<Vec>,
#[rasn(tag(1))]
pub public_key: Option,
}

impl PKCS8 {
pub fn print_pkcs8_info(&self) {
println!("PKCS8 key");
println!("Version; {}", self.version);
println!("AlgorithmIdentifier");
println!("\talgorithm: {:?}",self.algorithm.algorithm);
match self.algorithm.parameters.clone() {
None => {},
Some(parameters) => { println!("\tparameters: {:?}", &parameters )}
};
println!("Key: {:?}", format_serial(&self.private_key));
let tst = self.public_key.clone();
let tst1 = tst.unwrap();
println!("Pub: {:?}", format_serial(&tst1.as_raw_slice()));

}

}

pub fn rd_keys(file: &String) -> PKCS8 {
let pkcs8name = file.clone() + &".pkcs8".to_string();
let pubname = file.clone() + &".pub".to_string();

let pemkey = read_to_string(&pkcs8name).expect("can't read key file");
let pempub = read_to_string(&pubname).expect("can't read pub file");
let derpub= parse(pempub).expect("Not Valid pub Pem");
let derkey = parse(pemkey).expect("Not valid key Pem");

let mut privkey: PKCS8  = rasn::der::decode(&derkey.contents).expect("no decode pkcs8");
let pubkey: SubjectPublicKeyInfo = rasn::der::decode(&derpub.contents).expect("no decode pkcs8");

privkey.public_key = Some(pubkey.subject_public_key);

privkey

}
The output for ed25519

PKCS8 key
Version; 1
AlgorithmIdentifier
algorithm: ObjectIdentifier([1, 3, 101, 112])
Key: "04:20:ed:55:ba:91:91:a5:7a:73:4a:2c:57:63:28:65:bd:78:e3:7c:65:e8:51:ca:30:db:b8:1d:be:9e:25:c5:17:72"
Pub: "5f:72:cf:a6:5b:a9:0c:46:1c:10:ce:25:38:f9:9b:78:17:e3:e8:1c:27:48:db:85:5c:7b:72:e5:4e:9c:1b:12"

@greenpdx
Copy link
Author

greenpdx commented Feb 7, 2023

I am working on CSR attributes. It is an option and there needs to be a [0] in the ASN1

#[derive(AsnType, Clone, Debug, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CertificateRequestInfo {
//#[rasn(tag(explicit(0)), default)]
pub version: Version,
pub subject: Name,
pub subject_public_key_info: SubjectPublicKeyInfo,
#[rasn(tag(0))] <- this does nothing
pub attributes: Option
}
How do I add a [0] in front of attributes:

SEQUENCE (2 elem) SubjectPublicKey
SEQUENCE (1 elem)
OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
BIT STRING (256 bit) 0101111101110010110011111010011001011011101010010000110001000110000111…
[0] (1 elem)
SEQUENCE (2 elem) attributes
OBJECT IDENTIFIER 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)

@XAMPPRocky
Copy link
Collaborator

@greenpdx It would be very helpful if you could use code blocks and formatting in your comments as it would make them easier for me to read.

#[rasn(tag(0))] <- this does nothing

Are you sure? That's how it's done everywhere else in the codebase. Could you run cargo expand (you may need to install it) on the module to see what the generated code looks like?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/standard Related to a ASN.1 standard implemented in rasn. good first issue Good for newcomers help wanted Extra attention is needed kind/enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants