ASN1C v. 6.4 (in beta as of this writing) adds support for MDER.  This blog entry aims to show how to encode and decode MDER encoded data that itself contains MDER encoded data.  First, just a little background information.

What is MDER?

IEEE 11073 standards are concerned with enabling communication between medical, health care and wellness devices.   IEEE Std 11073-20101 defines the Medical Device Encoding Rules (MDER).  MDER is an optimized set of encoding rules that supports a subset of ASN.1 syntax.  The 11073 standards are restricted to using the subset of ASN.1 supported by MDER.

Holes That Need Filling

IEEE Std 11073-20601 defines some common data types, using ASN.1, for use with personal health devices.  These data types frequently use types OCTET STRING or ANY DEFINED BY to provide "holes" in the data types that can be filled in with varying types of data.  The value of these hole fields will be the MDER encoding of the ASN.1 value that is chosen to fill in the hole. For example, consider the DataProto type:

DataProto ::= SEQUENCE {
data-proto-id DataProtoId,
data-proto-info ANY DEFINED BY data-proto-id }

According to the description of DataProto, data-proto-info may be either empty, or the MDER encoding of either a PhdAssociationInformation or ManufSpecAssociationInformation.  The value of data-proto-id is supposed to tell us what will be in data-proto-info.

Encoding With Holes, Or Two-Phase Encoding

Encoding these cases is fairly simple.  To summarize the steps:

  1. Populate your inner type with data.  For example, create a PhdAssociationInformation and populate it.
  2. Encode your inner type into a memory buffer.
  3. Populate your outer type.  Assign the contents of the above memory buffer into the hole field of the outer type.
  4. Encode your outer type.

Some people call this "two phase encoding" since you encode the overall message in two steps.  Note, however, that nothing prevents this from becoming "N phase encoding": types may be nested to an arbitrary depth. Now we'll look at some sample Java code; you would do something similar for other languages.

//Our main output stream is going to a file
Asn1MderOutputStream encodeStream = new Asn1MderOutputStream (
   new FileOutputStream (filename));

//Our secondary stream goes to a memory buffer (a ByteArrayOutputStream)
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Asn1MderOutputStream encodeStream2 = new Asn1MderOutputStream( buffer );

//Create and populate the PhdAssociationInformation
PhdAssociationInformation phdAssocInfo = new PhdAssociationInformation();
phdAssocInfo.protocol_version = new ProtocolVersion(32,
              new byte[] { 0x40, 0x00, 0x00, 0x00 });
// ...populate other fields of phdAssocInfo

//Encode the phdAssocInfo into a buffer and then assign the encoding
//into an Asn1OpenType, which will go into the outer message
Asn1OpenType encodedPhdAssocInfo;
phdAssocInfo.encode(encodeStream2, /*useCachedLength=*/false);
encodedPhdAssocInfo = new Asn1OpenType( buffer.toByteArray() );
buffer.reset();     //We can now use encodeStream2 for another message

//Create and populate the outer type.  We're building an association request.
ApduType apduType;
apduType = new ApduType();
AarqApdu aarq = new AarqApdu();
apduType.set_aarq( aarq );
aarq.assoc_version = new AssociationVersion("'80000000'H");
aarq.data_proto_list = new DataProtoList(1);
aarq.data_proto_list.elements[0] = new DataProto();
aarq.data_proto_list.elements[0].data_proto_id =
         new DataProtoId(DataProtoId.data_proto_id_20601);

//Assign the encoded PhdAssociationInformation into the hole "data_proto_info"
aarq.data_proto_list.elements[0].data_proto_info = encodedPhdAssocInfo;

//Finally, encode the outer type to the main encode stream.
apduType.encode(encodeStream, /*useCachedLength=*/false);

Decoding: Do the Reverse

Not surprisingly, to decode you simply do things in reverse:

  1. Decode the outer type.
  2. Locate the hole field and create a decode buffer on its contents.
  3. Decode the value of the hole field using the inner type.

We'll again look at some Java code, but you would do something similar for other languages.

//create a decode buffer on the source of the outer message
Asn1MderDecodeBuffer decodeBuffer = new Asn1MderDecodeBuffer (...);

// decode the outer type
ApduType value = new ApduType ();
value.decode (decodeBuffer);

//locate the nested data (we're assuming here we know
//what is in the message so we aren't checking ids)
AarqApdu aarq = (AarqApdu) value.getElement();
for(int i = 0; i < aarq.data_proto_list.elements.length; i++)
{
   DataProto dataProto = aarq.data_proto_list.elements[i]);

   //create a decode buffer on the encoded data in data_proto_info
   Asn1MderDecodeBuffer buffer = new Asn1MderDecodeBuffer(
                  dataProto.data_proto_info.value);

   //decode the PhdAssociationInformation out of the data_proto_info
   PhdAssociationInformation phdAssocInfo = new PhdAssociationInformation();
   phdAssocInfo.decode(buffer);

   //...do whatever processing with the data in phdAssocInfo

}

And, that's all!


Published

Category

ASN1C

Tags