indutny / asn1.js

ASN.1 Decoder/Encoder/DSL
MIT License
181 stars 64 forks source link

A way to reuse generic definition with custom parts #14

Open Vanuan opened 10 years ago

Vanuan commented 10 years ago

There are parts of ASN.1 definition which content depends on the object identifier.

For example, to encode/decode PKCS7 ContentInfo we need 2 steps:

  1. decode generic ContentInfo
  2. decode specific part based on content type using specific model.

http://tools.ietf.org/html/rfc2315#section-7

ContentInfo ::= SEQUENCE {
   contentType ContentType,
   content
     [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }

Is there a way to define ASN.1 js model to do that in one step? I.e. some kind of conditional typing/objid mapping, maybe...

The same problem goes with AlgorithmIdentifier.

Vanuan commented 10 years ago

Say, something like this:

var PKCS7_CONTENT_TYPES = {
    "1 2 840 113549 1 7 1": "data",
    "1 2 840 113549 1 7 2": "signedData",
    "1 2 840 113549 1 7 3": "envelopedData",
    "1 2 840 113549 1 7 4": "signedAndEnvelopedData",
    "1 2 840 113549 1 7 5": "digestData",
    "1 2 840 113549 1 7 6": "encryptedData",
};
var EnvelopedData = ...
var SignedData = ...
var ContentInfo = asn1.define('ContentInfo', function() {
    this.seq().obj(
        this.key('contentType').objid(PKCS7_CONTENT_TYPES),
        this.key('content').optional().explicit(0).anydefinedby('contentType', {
            ...
            'signedData': this.use(SignedData),
            'envelopedData': this.use(EnvelopedData),
            ...
        })
    );
});

Notice the anydefinedby tag I propose.

muromec commented 10 years ago

Well, we can just pass some function to use() that would return another model. Something like

var ContenModels = {
    signedData: SignedData,
    envelopedData: EnvelopedData
};

var ContentInfo = asn1.define('ContentInfo', function() {
    this.seq().obj(
        this.key('contentType').objid(PKCS7_CONTENT_TYPES),
        this.key('content').optional().explicit(0).use(function () {
            return ContenModels[this.contentType];
       })
    );
});
Vanuan commented 10 years ago

Great! P.S. but it's not so declarative and doesn't intuitively match ASN.1 notation.

Vanuan commented 10 years ago

A bit more declarative way:

var ContentInfo = asn1.define('ContentInfo', function() {
    this.seq().obj(
        this.key('contentType').objid(PKCS7_CONTENT_TYPES),
        this.key('content').optional().explicit(0).use(function () {
            return {
                signedData: SignedData,
                envelopedData: EnvelopedData
            }[this.contentType];
        })
    );
});

Or even this:

var ContentInfo = asn1.define('ContentInfo', function() {
    this.seq().obj(
        this.key('contentType').objid(PKCS7_CONTENT_TYPES),
        this.key('content').optional().explicit(0).definedby('contentType', {
            signedData: SignedData,
            envelopedData: EnvelopedData
        })
    );
});

// Somewhere in ans1js
Node.prototype.definedby = function(tagName,  mapping) {
    return this.use(function() {
        return mapping[this[tagName]];
    });
}

Not sure what to do with error handling (if the key is missing).

felix commented 9 years ago

Is this considered fixed?