asn1: Update README-X681.md

This commit is contained in:
Nicolas Williams
2021-02-05 17:21:46 -06:00
parent 6cb0a2d667
commit cd1663b13e

View File

@@ -1,5 +1,14 @@
Bringing the Magical Power of X.681 (ASN.1 Information Object System) to Heimdal
================================================================================
#Bringing the Magical Power of X.681 (ASN.1 Information Object System) to Heimdal
##Table of Contents
- [Introduction](#Introduction)
- [Typed Holes / Open Types](#typed-holes--open-types)
- [ASN.1 IOS, Constraint, and Parameterization](#asn1-ios-constraint-and-parameterization)
- [IOS Crash Course](#ios-crash-course)
- [Implementation Thoughts](#implementation-thoughts)
##Introduction
The base of ASN.1 is specified by X.680, an ITU-T standard.
@@ -45,17 +54,6 @@ Besides a magical ASN.1 DER/JER dumper/transcoder utility, we want to replace
DN attribute and subject alternative name (SAN) `otherName` tables and much
hand-coded handling of certificate extensions in `lib/hx509/`.
ITU-T references:
https://www.itu.int/rec/T-REC-X.680-201508-I/en
https://www.itu.int/rec/T-REC-X.681-201508-I/en
https://www.itu.int/rec/T-REC-X.682-201508-I/en
https://www.itu.int/rec/T-REC-X.683-201508-I/en
Introduction
============
The reader should already be familiar with ASN.1, which anyways is a set of two
things:
@@ -73,9 +71,15 @@ otherwise very annoying task of dealing with "typed holes" / "open types".
The ASN.1 IOS is not sufficient to implement the magic we're after. Also
needed is constraint specification and parameterization of types.
ITU-T references:
Typed Holes / Open Types
========================
https://www.itu.int/rec/T-REC-X.680-201508-I/en
https://www.itu.int/rec/T-REC-X.681-201508-I/en
https://www.itu.int/rec/T-REC-X.682-201508-I/en
https://www.itu.int/rec/T-REC-X.683-201508-I/en
##Typed Holes / Open Types
A typed hole or open type is a pattern of data structure that generally looks
like:
@@ -252,8 +256,7 @@ display or compile DER (and other encodings) of certifcates and many other
interesting data structures.
ASN.1 IOS, Constraint, and Parameterization
===========================================
##ASN.1 IOS, Constraint, and Parameterization
The ASN.1 IOS is additional syntax that allows ASN.1 module authors to express
all the details about typed holes that ASN.1 compilers need to make developers'
@@ -268,9 +271,16 @@ RFC5912 has lots of examples, such as this `CLASS` corresponding to the
EXTENSION ::= CLASS {
-- The following are fields of a class (as opposed to "members" of
-- SEQUENCE or SET types):
&id OBJECT IDENTIFIER UNIQUE, -- UNIQUE -> this is the hole type ID.
&id OBJECT IDENTIFIER UNIQUE, -- This is a fixed-type value field.
-- UNIQUE -> There can be only one
-- object with this OID
-- in any object set of
-- this class.
-- I.e., this is like a
-- PRIMARY KEY in a SQL
-- TABLE spec.
&ExtnType, -- This is a type field (the hole).
&Critical BOOLEAN DEFAULT {TRUE | FALSE } -- this is a value field.
&Critical BOOLEAN DEFAULT {TRUE | FALSE } -- fixed-type value set field.
} WITH SYNTAX {
-- This is a specification of easy to use (but hard-to-parse) syntax for
-- specifying instances of this CLASS:
@@ -385,98 +395,7 @@ an `Extension` with `extnID == id-ce-authorityKeyIdentifier` must have an
`extnValue` of type `AuthorityKeyIdentifier`.
Implementation Thoughts
=======================
- The required specifications, X.681, X.682, and X.683, are fairly large and
non-trivial. Perhaps we can implement just the subset of those three that
we need to implement PKIX, just as we already implement just the subset of
X.680 that we need to implement PKIX and Kerberos.
For dealing with PKIX, the bare minimum of IOS classes we should want are:
- `ATTRIBUTE` (used for `DN` attributes in RFC5280)
- `EXTENSION` (used for certificate extensions in RFC5280)
- `TYPE-IDENTIFIER` (used for `OtherName` and for CMS' `Content-Type`)
The minimal subset of X.681, X.682, and X.683 needed to implement those
three is all we need. Eventually we may want to increase that subset so as
to implement other IOS classes from PKIX, such as `DIGEST-ALGORITHM`
Note that there's no object set specified for OTHER-NAME instances, but we
can create our own, and will. We want magic open type decoding to recurse
all the way down and handle DN attributes, extensions, SANs, policy
qualifiers, the works.
- We'll really want to do this mainly for the template compiler and begin
abandoning the original compiler -- hacking on two compilers is difficult,
and the template compiler is superior just on account of emitted code size
scaling as `O(N)` instead of `O(M * N)` where `M` is the number of encoding
rules supported and `N` is the number of types in an ASN.1 module (or all
modules).
- Also, to make the transition to using IOS in-tree, we'll want to keep
existing fields of C structures as generated by the compiler today, only
adding new ones, that way code that hasn't been updated to use the automatic
encoding/decoding can still work and we can then update Heimdal in-tree
slowly to take advantage of the new magic.
Thus `Extension` should compile to:
```C
typedef struct Extension {
-- Existing fields:
heim_oid extnID;
int *critical;
heim_octet_string extnValue;
-- New, CHOICE-like fields:
enum Extension_iosnum {
Extension_iosnumunknown = 0, /* when the extnID is unrecognized */
Extension_iosnum_ext_AuthorityKeyIdentifier = 1,
Extension_iosnum_ext_ext-SubjectKeyIdentifier = 2,
...
} _ios_element;
union {
heim_octet_string *_value;
authorityKeyIdentifier AuthorityKeyIdentifier;
subjectKeyIdentifier SubjectKeyIdentifier;
...
} _ios_u;
} Extension;
```
If a caller to `encode_Certificate()` passes a certificate object with
extensions with `_ioselement == Extension_iosnumunknown`, then the encoder
should use the `extnID` and `extnValue` fields, otherwise it should use the
`_ioselement` and `_iosu` fields and ignore the `extnID` and `extnValue`
fields.
In both cases, the `critical` field should get used as-is. The rule should
be that we support *two* special fields: a hole type ID enum field, and a
decoded hole value union. All other fields will map to either normal
members of the SET/SEQUENCE, or to members that are derived from a CLASS but
which are neither hole type ID fields nor hole fields.
- Type ID values must get mapped to discrete enum values. We'll want type IDs
to be sorted, too, so that we can binary search the "object set" when
decoding. For encoding we'll want to "switch" on the mapped type ID enum.
- The ASN.1 parser merely builds an AST. That will not change.
- The C header generator will remain shared between the two backends.
- Only the template backend will support the ASN.1 IOS. We'll basically
encode a new template for the combination of object set and typed hole
container type. This will come with a header entry indicating how many
items are in the object set, and each item will be one entry pointing to the
template for one particular object in the object set. The template for each
object will identify the type ID and the template for the associated type.
Perhaps we'll inline the objects for locality of reference.
IOS Crash Course
================
###IOS Crash Course
The ASN.1 IOS is... a bit difficult to understand. X.681 has a lot of strange
terminology, like "variable type value set field". An IOS "class" has fields,
@@ -581,6 +500,10 @@ Here's a simple example from PKIX:
- The `&Critical` field is a fixed-type value set field. We can tell because
it specifies a type (`BOOLEAN`) and starts with an upper-case letter.
In-tree we could avoid having to implement fixed-type value set fields by
renaming this one to `&critical` and eliding its `DEFAULT <ValueSet>` given
that we know there are only two possible values for a `BOOLEAN` field.
- Ignore the `WITH SYNTAX` clause for now. All it does is specify a
user-friendly butimplementor-hostile syntax for specifying objects for this
class.
@@ -731,3 +654,93 @@ cryptographic algorithm parameters, but also to provide those rules to other
code in the application so that the programmer doesn't have to manually write
the same in C, C++, Java, etc, and can refer to them when applying those
cryptographic algorithms.
##Implementation Thoughts
- The required specifications, X.681, X.682, and X.683, are fairly large and
non-trivial. Perhaps we can implement just the subset of those three that
we need to implement PKIX, just as we already implement just the subset of
X.680 that we need to implement PKIX and Kerberos.
For dealing with PKIX, the bare minimum of IOS classes we should want are:
- `ATTRIBUTE` (used for `DN` attributes in RFC5280)
- `EXTENSION` (used for certificate extensions in RFC5280)
- `TYPE-IDENTIFIER` (used for `OtherName` and for CMS' `Content-Type`)
The minimal subset of X.681, X.682, and X.683 needed to implement those
three is all we need. Eventually we may want to increase that subset so as
to implement other IOS classes from PKIX, such as `DIGEST-ALGORITHM`
Note that there's no object set specified for OTHER-NAME instances, but we
can create our own, and will. We want magic open type decoding to recurse
all the way down and handle DN attributes, extensions, SANs, policy
qualifiers, the works.
- We'll really want to do this mainly for the template compiler and begin
abandoning the original compiler -- hacking on two compilers is difficult,
and the template compiler is superior just on account of emitted code size
scaling as `O(N)` instead of `O(M * N)` where `M` is the number of encoding
rules supported and `N` is the number of types in an ASN.1 module (or all
modules).
- Also, to make the transition to using IOS in-tree, we'll want to keep
existing fields of C structures as generated by the compiler today, only
adding new ones, that way code that hasn't been updated to use the automatic
encoding/decoding can still work and we can then update Heimdal in-tree
slowly to take advantage of the new magic.
Thus `Extension` should compile to:
```C
typedef struct Extension {
-- Existing fields:
heim_oid extnID;
int *critical;
heim_octet_string extnValue;
-- New, CHOICE-like fields:
enum Extension_iosnum {
Extension_iosnumunknown = 0, /* when the extnID is unrecognized */
Extension_iosnum_ext_AuthorityKeyIdentifier = 1,
Extension_iosnum_ext_ext-SubjectKeyIdentifier = 2,
...
} _ios_element;
union {
heim_octet_string *_value;
authorityKeyIdentifier AuthorityKeyIdentifier;
subjectKeyIdentifier SubjectKeyIdentifier;
...
} _ios_u;
} Extension;
```
If a caller to `encode_Certificate()` passes a certificate object with
extensions with `_ioselement == Extension_iosnumunknown`, then the encoder
should use the `extnID` and `extnValue` fields, otherwise it should use the
`_ioselement` and `_iosu` fields and ignore the `extnID` and `extnValue`
fields.
In both cases, the `critical` field should get used as-is. The rule should
be that we support *two* special fields: a hole type ID enum field, and a
decoded hole value union. All other fields will map to either normal
members of the SET/SEQUENCE, or to members that are derived from a CLASS but
which are neither hole type ID fields nor hole fields.
- Type ID values must get mapped to discrete enum values. We'll want type IDs
to be sorted, too, so that we can binary search the "object set" when
decoding. For encoding we'll want to "switch" on the mapped type ID enum.
- The ASN.1 parser merely builds an AST. That will not change.
- The C header generator will remain shared between the two backends.
- Only the template backend will support the ASN.1 IOS. We'll basically
encode a new template for the combination of object set and typed hole
container type. This will come with a header entry indicating how many
items are in the object set, and each item will be one entry pointing to the
template for one particular object in the object set. The template for each
object will identify the type ID and the template for the associated type.
Perhaps we'll inline the objects for locality of reference.