diff options
Diffstat (limited to 'tcllib/modules/asn')
-rw-r--r-- | tcllib/modules/asn/ChangeLog | 272 | ||||
-rw-r--r-- | tcllib/modules/asn/asn.bench | 116 | ||||
-rw-r--r-- | tcllib/modules/asn/asn.man | 464 | ||||
-rw-r--r-- | tcllib/modules/asn/asn.pcx | 271 | ||||
-rw-r--r-- | tcllib/modules/asn/asn.tcl | 1580 | ||||
-rw-r--r-- | tcllib/modules/asn/asn.test | 956 | ||||
-rw-r--r-- | tcllib/modules/asn/laymans_guide.txt | 1855 | ||||
-rw-r--r-- | tcllib/modules/asn/pkgIndex.tcl | 4 |
8 files changed, 5518 insertions, 0 deletions
diff --git a/tcllib/modules/asn/ChangeLog b/tcllib/modules/asn/ChangeLog new file mode 100644 index 0000000..a38aad7 --- /dev/null +++ b/tcllib/modules/asn/ChangeLog @@ -0,0 +1,272 @@ +2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.15 ======================== + * + +2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.14 ======================== + * + +2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.13 ======================== + * + +2011-01-05 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl : Fixed wrong asnGetBigInteger code + asn.man : Tcllib SF [3039090], added tests. + asn.test : Patchlevel now 0.8.4. + asn.bench: Thank to Roy Keene for the patch. + asn.pcx : + +2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.12 ======================== + * + +2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11.1 ======================== + * + +2008-11-22 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.bench: Added a new bench for OID decoding + +2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11 ======================== + * + +2008-06-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.pcx: New file. Syntax definitions for the public commands of + the asn package. + +2008-03-27 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.man: Added documentation for a number of public yet not + documented. + +2008-03-09 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl : Added (optional ) support for Tcl 8.5 unsigned binary + asn.man : scan to speed up byte extraction. + pkgIndex.tcl: + +2007-09-18 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl : Fixed missing padding in bitstring encoder + asn.man : Tcllib SF [1797428], added some tests for + asn.test : Bitstrings. Patchlevel now 0.8.2. + +2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.10 ======================== + * + +2007-04-20 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl : Fixed misspelt PRIVATE name in docs and code, + asn.man : Tcllib SF [1704626]. + asn.test : Fixed various glitches in the new commands and + added more tests for asnPeekTag and asnTag. + Version raised to 0.8.1. + +2007-04-20 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl : Fixed documentation bug Tcllib [1703408]. + asn.man : Fixed parts of [1558351] by adding handling + asn.bench : for tag numbers > 31 and some new commands + asn.test : for changing tags. + Fixed Bug [1645333]. Some new benchmarks. + Raised Version to 0.8 because of new commands. + +2007-03-19 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.man: Fixed all warnings due to use of now deprecated + commands. Added a section about how to give feedback. + +2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.9 ======================== + * + +2006-09-01 Michael Schlenker <mic42@users.sourceforge.net> + * asn.tcl : Added asnRetag, asnPeekByte and asnGetLength + asn.man : to the public api, because those are needed by + pkgIndex.tcl: the ldap package. + +2006-08-16 Michael Schlenker <mic42@users.sourceforge.net> + * asn.tcl : Fixed stupid typos in asnGetApplication. + Fixes bug Tcllib [1541436]. + +2006-08-15 Michael Schlenker <mic42@users.sourceforge.net> + * asn.tcl : added two convenience functions, + asn.man : asnSequenceFromList and asnSetFromList for + pkgIndex.tcl: use by the ldap module. Raised version + to 0.6 + +2006-08-15 Michael Schlenker <mic42@users.sourceforge.net> + * asn.test: Added more tests. Fixed wrong version + asn.man : reference in man page. + +2006-08-13 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl : Fixed Tcllib Bug [1539479]. Package version + asn.test: raised to 0.5.2. Added smoketest for the bug. + Thanks to Pierre David for finding this. + +2006-03-22 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: Removed dependency on the log package to fix + Tcllib Bug [1408807]. Package version raised to 0.5.1. + +2006-01-29 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.test: Fixed use of duplicate test names. + +2006-01-22 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.test: More boilerplate simplified via use of test support. + +2006-01-19 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.test: Hooked into the new common test support code. + +2006-01-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.man: Reworked the documentation of the new commands a bit, + and fixed some typos in words and use of the doctools commands. + +2006-01-16 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: Added new decoders for BMPString + asn.man: and UTF8String. New convenience + asn.test: function asnGetString. Added tests + pkgIndex.tcl: for OIDs and the new string functions. + Bugfix for wrong tag in asnNumericString. + Version increased to 0.5. + Big thanks to Victor Wagner <vitus@45.free.net) + for the patches. + +2006-01-05 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: Fixed Tcllib Bug #1393804. Stupid typo. + asn.test: Added some tests for asnPeekByte, + asnGetByte, asnGetBytes. + pkgIndex.tcl: Version increased to 0.4.2 + +2005-12-30 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: Applied patch from Tcllib Bug #1391776 to + create better (shorter) encodings for OIDs. + pkgIndex.tcl: Version number increased to 0.4.1 + +2005-10-21 Andreas Kupries <andreask@activestate.com> + + * asn.test: Fixed typos, new and old. + +2005-10-18 Andreas Kupries <andreask@activestate.com> + + * asn.test: Package requires 8.4, this was not caught + * pkgIndex.tcl: properly in index, nor in testsuite. + + * asn.bench: New file, benchmarks, only basics for now. + +2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.8 ======================== + * + +2005-04-04 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.man: Synchronized indexed/provided versions. + * pkgIndex.tcl: + +2005-02-09 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: + * asn.man: + * asn.test: Fixed incorrect length encoding and decoding . + Added 64-bit support for length encoding/decoding, + Added 64-bit support for integers. + Added tests for length encoding/decoding (10.x-11.x). + Added tests for 64-bit integers. + Bumped version number to 0.4 + The package now needs Tcl 8.4 for wide() support. + +2004-12-29 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: + * asn.man: + * asn.test: Added more encoder functions + to the module. + * Added math::bignum support for encoding + and decoding large integers. Interface not + final, so no docs yet. + * Fixed bug with negative 3-byte integers in + asn::asnInteger, those were padded with 0x00 instead + of 0xff and added test for this (2.25). + * Added tests for null, boolean encoding and decoding + and for bignum integer encoding (5.x-9.x). + * Removed second signed-unsigned conversion for the tags + in error messages, asnGetByte returns unsigned tags. + * bumped version number to 0.3 + +2004-10-05 Michael Schlenker <mic42@users.sourceforge.net> + + * asn.tcl: Added more decoder functions to the module. + * asn.man: New primitve Types supported: + OBJECT IDENTIFIER,BIT STRING,UTCTIME,BOOLEAN, + PRINTABLE STRING, IA5 STRING, NULL + New structure element: Context tagging + +2004-10-05 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.7 ======================== + * + +2004-08-15 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.tcl: Typo police. + * asn.man: + * ChangeLog: + +2004-08-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.test: + * asn.tcl: Enhanced encoder for enumerations: fixed buglet for + 2-byte integers (not minimal in border case 127), added the + handling of 3-byte integers. Extended testsuite to cover + enumerations as well. + +2004-08-05 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * asn.test: New file, first test cases for asn package. + + * asn.tcl: Enhanced integer encoder: fixed buglet for 2-byte + integers (not minimal in bordercase 127), added the handling of + 3-byte integers. + +2004-05-27 Andreas Kupries <andreask@activestate.com> + + * New module: ASN.1 de- and encoding. This was provided to us + indirectly by Jochen Loewer <loewerj@web.de>, through the LDAP + module. It contains the same functionality internally. + + * Added doctools documentation. Referenced the "Layman's Guide to + a Subset of ASN.1, BER, and DER" and added text version to the + sources, to guide future work. diff --git a/tcllib/modules/asn/asn.bench b/tcllib/modules/asn/asn.bench new file mode 100644 index 0000000..cadac67 --- /dev/null +++ b/tcllib/modules/asn/asn.bench @@ -0,0 +1,116 @@ +# -*- tcl -*- +# Tcl Benchmark File +# +# This file contains a number of benchmarks for the 'asn' module. +# This allow developers to monitor/gauge/track package performance. +# +# (c) 2005 Andreas Kupries <andreas_kupries@users.sourceforge.net> + +# We need at least version 8.2 for the package and thus the +# benchmarks. + +if {![package vsatisfies [package provide Tcl] 8.4]} { + return +} + +# ### ### ### ######### ######### ######### ########################### +## Setting up the environment ... + +package forget log +catch {namespace delete ::log} +source [file join [file dirname [file dirname [info script]]] log log.tcl] + +package forget asn +catch {namespace delete ::asn} +source [file join [file dirname [info script]] asn.tcl] + +# ### ### ### ######### ######### ######### ########################### +## Benchmarks. + +foreach n { + -10 -100 -1000 -10000 -100000 -1000000 + 0 + 10 100 1000 10000 100000 1000000 +} { + bench -desc "ASN Integer $n" -body { + asn::asnInteger $n + } + + bench -desc "ASN Enum $n" -body { + asn::asnEnumeration $n + } + + bench -desc "ASN Boolean $n" -body { + asn::asnBoolean $n + } +} + +foreach n {10 100 1000 10000} { + bench -desc "ASN OctetString ${n}" -pre { + set str [string repeat X $n] + } -body { + asn::asnOctetString $str + } -post { + unset str + } +} + +for {set n 1; set i 0} {$i < 64} { set n [expr {wide($n)*2}] ; incr i} { + bench -desc "ASN asnLength 2^${i}" -body { + asn::asnLength $n + } +} + +for {set n 0} {$n < 10} { incr n} { + bench -desc "ASN encode oid with ${n}+2 components" -pre { + set oid [list 1 10] + for {set i 0} {$i < $n} {incr i} { + lappend oid $i + } + } -body { + asn::asnObjectIdentifier $oid + } -post { + unset oid + } +} + +for {set n 0} {$n < 10} { incr n} { + bench -desc "ASN decode oid with ${n}+2 components" -pre { + set oid [list 1 10] + for {set i 0} {$i < $n} {incr i} { + lappend oid $i + } + set oidval [asn::asnObjectIdentifier $oid] + } -body { + asn::asnGetObjectIdentifier $oidval + } -post { + unset oid + unset oidval + } +} + +foreach n {10 100 1000 10000 100000} { + bench -desc "ASN asnGetByte ${n}" -pre { + set bytes [binary format a* [string repeat X $n]] + } -body { + ::asn::asnGetByte bytes dummy + } -post { + unset bytes + unset dummy + } +} + + +foreach n {10 100 1000 10000 100000} { + bench -desc "ASN asnGetBytes ${n} len 5" -pre { + set bytes [binary format a* [string repeat X $n]] + } -body { + ::asn::asnGetBytes bytes 5 dummy + } -post { + unset bytes + unset dummy + } +} + +# ### ### ### ######### ######### ######### ########################### +## Complete diff --git a/tcllib/modules/asn/asn.man b/tcllib/modules/asn/asn.man new file mode 100644 index 0000000..b5eb5c5 --- /dev/null +++ b/tcllib/modules/asn/asn.man @@ -0,0 +1,464 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin asn n 0.8] +[keywords asn] +[keywords ber] +[keywords cer] +[keywords der] +[keywords internet] +[keywords protocol] +[keywords x.208] +[keywords x.209] +[copyright {2004 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[copyright {2004 Jochen Loewer <loewerj@web.de>}] +[copyright {2004-2011 Michael Schlenker <mic42@users.sourceforge.net>}] +[moddesc {ASN.1 processing}] +[category Networking] +[titledesc {ASN.1 BER encoder/decoder}] +[require Tcl 8.4] +[require asn [opt 0.8.4]] +[description] +[para] + +The [package asn] package provides [emph partial] de- and encoder +commands for BER encoded ASN.1 data. It can also be used for +decoding DER, which is a restricted subset of BER. + +[para] + +ASN.1 is a standard [term {Abstract Syntax Notation}], and BER are its +[term {Basic Encoding Rules}]. + +[para] + +See [uri http://asn1.elibel.tm.fr/en/standards/index.htm] for more +information about the standard. + +[para] + +Also see [uri http://luca.ntop.org/Teaching/Appunti/asn1.html] for +[emph {A Layman's Guide to a Subset of ASN.1, BER, and DER}], an RSA +Laboratories Technical Note by Burton S. Kaliski Jr. (Revised November +1, 1993). A text version of this note is part of the module sources +and should be read by any implementor. + +[section {PUBLIC API}] +[subsection ENCODER] + +[list_begin definitions] + +[call [cmd ::asn::asnSequence] [arg evalue]...] + +Takes zero or more encoded values, packs them into an ASN sequence and +returns its encoded binary form. + +[call [cmd ::asn::asnSequenceFromList] [arg elist]] + +Takes a list of encoded values, packs them into an ASN sequence and +returns its encoded binary form. + +[call [cmd ::asn::asnSet] [arg evalue]...] + +Takes zero or more encoded values, packs them into an ASN set and +returns its encoded binary form. + +[call [cmd ::asn::asnSetFromList] [arg elist]] + +Takes a list of encoded values, packs them into an ASN set and +returns its encoded binary form. + +[call [cmd ::asn::asnApplicationConstr] [arg appNumber] [arg evalue]...] + +Takes zero or more encoded values, packs them into an ASN application +construct and returns its encoded binary form. + +[call [cmd ::asn::asnApplication] [arg appNumber] [arg data]] + +Takes a single encoded value [arg data], packs it into an ASN +application construct and returns its encoded binary form. + +[call [cmd ::asn::asnChoice] [arg appNumber] [arg evalue]...] + +Takes zero or more encoded values, packs them into an ASN choice +construct and returns its encoded binary form. + +[call [cmd ::asn::asnChoiceConstr] [arg appNumber] [arg evalue]...] + +Takes zero or more encoded values, packs them into an ASN choice +construct and returns its encoded binary form. + +[call [cmd ::asn::asnInteger] [arg number]] + +Returns the encoded form of the specified integer +[arg number]. + +[call [cmd ::asn::asnEnumeration] [arg number]] + +Returns the encoded form of the specified enumeration id +[arg number]. + +[call [cmd ::asn::asnBoolean] [arg bool]] + +Returns the encoded form of the specified boolean value +[arg bool]. + +[call [cmd ::asn::asnContext] [arg context] [arg data]] + +Takes an encoded value and packs it into a constructed value with +application tag, the [arg context] number. + +[call [cmd ::asn::asnContextConstr] [arg context] [arg evalue]...] + +Takes zero or more encoded values and packs them into a constructed +value with application tag, the [arg context] number. + +[call [cmd ::asn::asnObjectIdentifier] [arg idlist]] + +Takes a list of at least 2 integers describing an object identifier +(OID) value, and returns the encoded value. + +[call [cmd ::asn::asnUTCTime] [arg utcstring]] + +Returns the encoded form of the specified UTC time string. + +[call [cmd ::asn::asnNull]] + +Returns the NULL encoding. + +[call [cmd ::asn::asnBitString] [arg string]] + +Returns the encoded form of the specified [arg string]. + +[call [cmd ::asn::asnOctetString] [arg string]] + +Returns the encoded form of the specified [arg string]. + +[call [cmd ::asn::asnNumericString] [arg string]] + +Returns the [arg string] encoded as ASN.1 NumericString. Raises an +error if the [arg string] contains characters other than decimal +numbers and space. + +[call [cmd ::asn::asnPrintableString] [arg string]] + +Returns the [arg string] encoding as ASN.1 PrintableString. Raises an +error if the [arg string] contains characters which are not allowed by +the Printable String datatype. The allowed characters are A-Z, a-z, +0-9, space, apostrophe, colon, parentheses, plus, minus, comma, +period, forward slash, question mark, and the equals sign. + +[call [cmd ::asn::asnIA5String] [arg string]] + +Returns the [arg string] encoded as ASN.1 IA5String. Raises an error +if the [arg string] contains any characters outside of the US-ASCII +range. + +[call [cmd ::asn::asnBMPString] [arg string]] + +Returns the [arg string] encoded as ASN.1 Basic Multilingual Plane +string (Which is essentialy big-endian UCS2). + +[call [cmd ::asn::asnUTF8String] [arg string]] + +Returns the [arg string] encoded as UTF8 String. Note that some legacy +applications such as Windows CryptoAPI do not like UTF8 strings. Use +BMPStrings if you are not sure. + +[call [cmd ::asn::asnString] [arg string]] + +Returns an encoded form of [arg string], choosing the most restricted +ASN.1 string type possible. If the string contains non-ASCII +characters, then there is more than one string type which can be +used. See [cmd ::asn::defaultStringType]. + +[call [cmd ::asn::defaultStringType] [opt [arg type]]] + +Selects the string type to use for the encoding of non-ASCII +strings. Returns current default when called without argument. If the +argument [arg type] is supplied, it should be either [const UTF8] or +[const BMP] to choose UTF8String or BMPString respectively. + +[list_end] +[para] + +[subsection DECODER] + +General notes: + +[list_begin enumerated] +[enum] +Nearly all decoder commands take two arguments. These arguments are variable +names, except for [cmd ::asn::asnGetResponse]. The first variable +contains the encoded ASN value to decode at the beginning, and more, +and the second variable is where the value is stored to. The remainder +of the input after the decoded value is stored back into the +datavariable. + +[enum] +After extraction the data variable is always modified first, before by +writing the extracted value to its variable. This means that if both +arguments refer to the same variable, it will always contain the +extracted value after the call, and not the remainder of the input. + +[list_end] + +[para] +[list_begin definitions] +[call [cmd ::asn::asnPeekByte] [arg data_var] [arg byte_var]] + +Retrieve the first byte of the data, without modifing [arg data_var]. +This can be used to check for implicit tags. + +[call [cmd ::asn::asnGetLength] [arg data_var] [arg length_var]] + +Decode the length information for a block of BER data. The tag has already +to be removed from the data. + +[call [cmd ::asn::asnGetResponse] [arg chan] [arg data_var]] + +Reads an encoded ASN [emph sequence] from the channel [arg chan] and +stores it into the variable named by [arg data_var]. + +[call [cmd ::asn::asnGetInteger] [arg data_var] [arg int_var]] + +Assumes that an encoded integer value is at the front of the data +stored in the variable named [arg data_var], extracts and stores it +into the variable named by [arg int_var]. Additionally removes all +bytes associated with the value from the data for further processing +by the following decoder commands. + +[call [cmd ::asn::asnGetEnumeration] [arg data_var] [arg enum_var]] + +Assumes that an enumeration id is at the front of the data stored in +the variable named [arg data_var], and stores it into the variable +named by [arg enum_var]. Additionally removes all bytes associated +with the value from the data for further processing by the following +decoder commands. + +[call [cmd ::asn::asnGetOctetString] [arg data_var] [arg string_var]] + +Assumes that a string is at the front of the data stored in the +variable named [arg data_var], and stores it into the variable named +by [arg string_var]. Additionally removes all bytes associated with +the value from the data for further processing by the following +decoder commands. + +[call [cmd ::asn::asnGetString] [arg data_var] [arg string_var] [opt [arg type_var]]] + +Decodes a user-readable string. This is a convenience function which +is able to automatically distinguish all supported ASN.1 string types +and convert the input value appropriately. + +See [cmd ::asn::asnGetPrintableString], [cmd ::asnGetIA5String], etc. +below for the type-specific conversion commands. + +[para] + +If the optional third argument [arg type_var] is supplied, then the +type of the incoming string is stored in the variable named by it. + +[para] + +The function throws the error + +"Invalid command name asnGetSome[var UnsupportedString]" if the +unsupported string type [var Unsupported] is encountered. You can +create the appropriate function + +"asn::asnGetSome[var UnsupportedString]" in your application if +neccessary. + +[call [cmd ::asn::asnGetNumericString] [arg data_var] [arg string_var]] + +Assumes that a numeric string value is at the front of the data stored +in the variable named [arg data_var], and stores it into the variable +named by [arg string_var]. Additionally removes all bytes associated +with the value from the data for further processing by the following +decoder commands. + +[call [cmd ::asn::asnGetPrintableString] [arg data_var] [arg string_var]] + +Assumes that a printable string value is at the front of the data +stored in the variable named [arg data_var], and stores it into the +variable named by [arg string_var]. Additionally removes all bytes +associated with the value from the data for further processing by the +following decoder commands. + +[call [cmd ::asn::asnGetIA5String] [arg data_var] [arg string_var]] + +Assumes that a IA5 (ASCII) string value is at the front of the data +stored in the variable named [arg data_var], and stores it into the +variable named by [arg string_var]. Additionally removes all bytes +associated with the value from the data for further processing by the +following decoder commands. + +[call [cmd ::asn::asnGetBMPString] [arg data_var] [arg string_var]] + +Assumes that a BMP (two-byte unicode) string value is at the front of +the data stored in the variable named [arg data_var], and stores it +into the variable named by [arg string_var], converting it into a +proper Tcl string. Additionally removes all bytes associated with the +value from the data for further processing by the following decoder +commands. + +[call [cmd ::asn::asnGetUTF8String] [arg data_var] [arg string_var]] + +Assumes that a UTF8 string value is at the front of the data stored in +the variable named [arg data_var], and stores it into the variable +named by [arg string_var], converting it into a proper Tcl string. +Additionally removes all bytes associated with the value from the data +for further processing by the following decoder commands. + +[call [cmd ::asn::asnGetUTCTime] [arg data_var] [arg utc_var]] + +Assumes that a UTC time value is at the front of the data stored in the +variable named [arg data_var], and stores it into the variable named +by [arg utc_var]. The UTC time value is stored as a string, which has to +be decoded with the usual clock scan commands. +Additionally removes all bytes associated with the +value from the data for further processing by the following decoder +commands. + +[call [cmd ::asn::asnGetBitString] [arg data_var] [arg bits_var]] + +Assumes that a bit string value is at the front of the data stored in the +variable named [arg data_var], and stores it into the variable named +by [arg bits_var] as a string containing only 0 and 1. +Additionally removes all bytes associated with the +value from the data for further processing by the following decoder +commands. + +[call [cmd ::asn::asnGetObjectIdentifier] [arg data_var] [arg oid_var]] + +Assumes that a object identifier (OID) value is at the front of the data +stored in the variable named [arg data_var], and stores it into the variable +named by [arg oid_var] as a list of integers. +Additionally removes all bytes associated with the +value from the data for further processing by the following decoder +commands. + +[call [cmd ::asn::asnGetBoolean] [arg data_var] [arg bool_var]] + +Assumes that a boolean value is at the front of the data stored in the +variable named [arg data_var], and stores it into the variable named +by [arg bool_var]. Additionally removes all bytes associated with the +value from the data for further processing by the following decoder +commands. + +[call [cmd ::asn::asnGetNull] [arg data_var]] + +Assumes that a NULL value is at the front of the data stored in the +variable named [arg data_var] and removes the bytes used to encode it +from the data. + +[call [cmd ::asn::asnGetSequence] [arg data_var] [arg sequence_var]] + +Assumes that an ASN sequence is at the front of the data stored in the +variable named [arg data_var], and stores it into the variable named +by [arg sequence_var]. Additionally removes all bytes associated with +the value from the data for further processing by the following +decoder commands. + +[para] + +The data in [arg sequence_var] is encoded binary and has to be +further decoded according to the definition of the sequence, using the +decoder commands here. + +[call [cmd ::asn::asnGetSet] [arg data_var] [arg set_var]] + +Assumes that an ASN set is at the front of the data stored in the +variable named [arg data_var], and stores it into the variable named +by [arg set_var]. Additionally removes all bytes associated with the +value from the data for further processing by the following decoder +commands. + +[para] + +The data in [arg set_var] is encoded binary and has to be further +decoded according to the definition of the set, using the decoder +commands here. + +[call [cmd ::asn::asnGetApplication] [arg data_var] [arg appNumber_var] [opt [arg content_var]] [opt [arg encodingType_var]]] + +Assumes that an ASN application construct is at the front of the data +stored in the variable named [arg data_var], and stores its id into +the variable named by [arg appNumber_var]. Additionally removes all +bytes associated with the value from the data for further processing +by the following decoder commands. + +If a [arg content_var] is specified, then the command places all data +associated with it into the named variable, in the binary form which +can be processed using the decoder commands of this package. + +If a [arg encodingType_var] is specified, then that var is set to 1 if +the encoding is constructed and 0 if it is primitive. + +[para] + +Otherwise it is the responsibility of the caller to decode the +remainder of the application construct based on the id retrieved by +this command, using the decoder commands of this package. + +[call [cmd ::asn::asnGetContext] [arg data_var] [arg contextNumber_var] [opt [arg content_var]] [opt [arg encodingType_var]]] + +Assumes that an ASN context tag construct is at the front of the data +stored in the variable named [arg data_var], and stores its id into +the variable named by [arg contextNumber_var]. Additionally removes all +bytes associated with the value from the data for further processing +by the following decoder commands. + +If a [arg content_var] is specified, then the command places all data +associated with it into the named variable, in the binary form which +can be processed using the decoder commands of this package. + +If a [arg encodingType_var] is specified, then that var is set to 1 if +the encoding is constructed and 0 if it is primitive. + +[para] + +Otherwise it is the responsibility of the caller to decode the +remainder of the construct based on the id retrieved by this command, +using the decoder commands of this package. + +[list_end] +[para] +[subsection {HANDLING TAGS}] + +Working with ASN.1 you often need to decode tagged values, which use a tag thats different +from the universal tag for a type. In those cases you have to replace the tag with the universal tag +used for the type, to decode the value. + +To decode a tagged value use the [cmd ::asn::asnRetag] to change the tag to the appropriate type +to use one of the decoders for primitive values. + +To help with this the module contains three functions: + +[list_begin definitions] +[call [cmd ::asn::asnPeekTag] [arg data_var] [arg tag_var] [arg tagtype_var] [arg constr_var]] + +The [cmd ::asn::asnPeekTag] command can be used to take a peek at the data and decode the tag value, without +removing it from the data. The [arg tag_var] gets set to the tag number, while the [arg tagtype_var] gets set +to the class of the tag. (Either UNIVERSAL, CONTEXT, APPLICATION or PRIVATE). The [arg constr_var] is set to 1 if the +tag is for a constructed value, and to 0 for not constructed. It returns the length of the tag. + +[call [cmd ::asn::asnTag] [arg tagnumber] [opt [arg class]] [opt [arg tagstyle]]] + +The [cmd ::asn::asnTag] can be used to create a tag value. The [arg tagnumber] gives the number of the tag, while +the [arg class] gives one of the classes (UNIVERSAL,CONTEXT,APPLICATION or PRIVATE). The class may be abbreviated to just the first letter (U,C,A,P), +default is UNIVERSAL. +The [arg tagstyle] is either C for Constructed encoding, or P for primitve encoding. It defaults to P. You can also use 1 instead of C and +0 instead of P for direct use of the values returned by [cmd ::asn::asnPeekTag]. + +[call [cmd ::asn::asnRetag] [arg data_var] [arg newTag]] + +Replaces the tag in front of the data in [arg data_var] with [arg newTag]. The new Tag can be created using the [cmd ::asn::asnTag] command. +[list_end] + +[section EXAMPLES] + +Examples for the usage of this package can be found in the +implementation of package [package ldap]. + +[vset CATEGORY asn] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/asn/asn.pcx b/tcllib/modules/asn/asn.pcx new file mode 100644 index 0000000..2114928 --- /dev/null +++ b/tcllib/modules/asn/asn.pcx @@ -0,0 +1,271 @@ +# -*- tcl -*- asn.pcx +# Syntax of the commands provided by package asn. +# +# For use by TclDevKit's static syntax checker (v4.1+). +# See http://www.activestate.com/solutions/tcl/ +# See http://aspn.activestate.com/ASPN/docs/Tcl_Dev_Kit/4.0/Checker.html#pcx_api +# for the specification of the format of the code in this file. +# + +package require pcx +pcx::register asn +pcx::tcldep 0.8.4 needs tcl 8.4 + +namespace eval ::asn {} + +pcx::message outOfMinRange {The number "%1$s" is below the allowed minimum of "%2$s"} err +pcx::message outOfMaxRange {The number "%1$s" is aboove the allowed maximum of "%2$s"} err +#pcx::scan <VERSION> <NAME> <RULE> + +pcx::check 0.8.3 std ::asn::asnApplication \ + {checkSimpleArgs 2 2 { + checkWholeNum + checkWord + }} +pcx::check 0.8.3 std ::asn::asnApplicationConstr \ + {checkSimpleArgs 1 -1 { + checkWholeNum + checkWord + }} +pcx::check 0.8.3 std ::asn::asnBMPString \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnBitString \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnBoolean \ + {checkSimpleArgs 1 1 { + checkBoolean + }} +pcx::check 0.8.3 std ::asn::asnChoice \ + {checkSimpleArgs 1 -1 { + checkWholeNum + checkWord + }} +pcx::check 0.8.3 std ::asn::asnChoiceConstr \ + {checkSimpleArgs 1 -1 { + checkWholeNum + checkWord + }} +pcx::check 0.8.3 std ::asn::asnContext \ + {checkSimpleArgs 2 2 { + checkWholeNum + checkWord + }} +pcx::check 0.8.3 std ::asn::asnContextConstr \ + {checkSimpleArgs 1 -1 { + checkWholeNum + checkWord + }} +pcx::check 0.8.3 std ::asn::asnEnumeration \ + {checkSimpleArgs 1 1 { + checkWholeNum + }} +pcx::check 0.8.3 std ::asn::asnGetApplication \ + {checkSimpleArgs 2 4 { + checkVarNameWrite + checkVarNameWrite + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetBMPString \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetBitString \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetBoolean \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetContext \ + {checkSimpleArgs 2 4 { + checkVarNameWrite + checkVarNameWrite + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetEnumeration \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetIA5String \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetInteger \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetLength \ + {checkSimpleArgs 2 2 { + checkVarname + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetNull \ + {checkSimpleArgs 1 1 { + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetNumericString \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetObjectIdentifier \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetOctetString \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetPrintableString \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetResponse \ + {checkSimpleArgs 2 2 { + checkChannelID + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetSequence \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetSet \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetString \ + {checkSimpleArgs 2 3 { + checkVarNameWrite + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetUTCTime \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnGetUTF8String \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnIA5String \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnInteger \ + {checkSimpleArgs 1 1 { + checkInt + }} +pcx::check 0.8.3 std ::asn::asnNull \ + {checkAtEnd} +pcx::check 0.8.3 std ::asn::asnNumericString \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnObjectIdentifier \ + {checkSimpleArgs 1 1 { + checkListValues 2 -1 { + {pcx::checkRange 0 2} + {pcx::checkRange 0 39} + checkWholeNum + } + }} +pcx::check 0.8.3 std ::asn::asnOctetString \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnPeekByte \ + {checkSimpleArgs 2 2 { + checkVarname + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnPeekTag \ + {checkSimpleArgs 4 4 { + checkVarname + checkVarNameWrite + checkVarNameWrite + checkVarNameWrite + }} +pcx::check 0.8.3 std ::asn::asnPrintableString \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnRetag \ + {checkSimpleArgs 2 2 { + checkVarNameWrite + checkWholeNum + }} +pcx::check 0.8.3 std ::asn::asnSequence \ + {checkSimpleArgs 0 -1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnSequenceFromList \ + {checkSimpleArgs 1 1 { + checkList + }} +pcx::check 0.8.3 std ::asn::asnSet \ + {checkSimpleArgs 0 -1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnSetFromList \ + {checkSimpleArgs 1 1 { + checkList + }} +pcx::check 0.8.3 std ::asn::asnString \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnTag \ + {checkSimpleArgs 1 3 { + checkWholeNum + {checkKeyword 1 {UNIVERSAL CONTEXT APPLICATION PRIVATE U C A P}} + {checkKeyword 1 {C P 0 1}} + }} +pcx::check 0.8.3 std ::asn::asnUTCTime \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::asnUTF8String \ + {checkSimpleArgs 1 1 { + checkWord + }} +pcx::check 0.8.3 std ::asn::defaultStringType \ + {checkSimpleArgs 0 1 { + {checkKeyword 1 {BMP UTF8}} + }} + +proc pcx::checkInitRange {min max t i} { + set w [lindex $t $i] + if {[getLiteral $w num] && ![catch {incr num 0}]} { + if {($min != {}) && ($num < $min)} { + logError pcx::outOfMinRange [getTokenRange $w] $num $min + } + if {($max != {}) && ($num > $max)} { + logError pcx::outOfMaxRange [getTokenRange $w] $num $min + } + } + return [checkInt $t $i] +} + +# Initialization via pcx::init. +# Use a ::asn::init procedure for non-standard initialization. +pcx::complete diff --git a/tcllib/modules/asn/asn.tcl b/tcllib/modules/asn/asn.tcl new file mode 100644 index 0000000..cca460a --- /dev/null +++ b/tcllib/modules/asn/asn.tcl @@ -0,0 +1,1580 @@ +#----------------------------------------------------------------------------- +# Copyright (C) 1999-2004 Jochen C. Loewer (loewerj@web.de) +# Copyright (C) 2004-2011 Michael Schlenker (mic42@users.sourceforge.net) +#----------------------------------------------------------------------------- +# +# A partial ASN decoder/encoder implementation in plain Tcl. +# +# See ASN.1 (X.680) and BER (X.690). +# See 'asn_ber_intro.txt' in this directory. +# +# This software is copyrighted by Jochen C. Loewer (loewerj@web.de). The +# following terms apply to all files associated with the software unless +# explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. +# +# written by Jochen Loewer +# 3 June, 1999 +# +# $Id: asn.tcl,v 1.20 2011/01/05 22:33:33 mic42 Exp $ +# +#----------------------------------------------------------------------------- + +# needed for using wide() +package require Tcl 8.4 + +namespace eval asn { + # Encoder commands + namespace export \ + asnSequence \ + asnSequenceFromList \ + asnSet \ + asnSetFromList \ + asnApplicationConstr \ + asnApplication \ + asnContext\ + asnContextConstr\ + asnChoice \ + asnChoiceConstr \ + asnInteger \ + asnEnumeration \ + asnBoolean \ + asnOctetString \ + asnNull \ + asnUTCTime \ + asnNumericString \ + asnPrintableString \ + asnIA5String\ + asnBMPString\ + asnUTF8String\ + asnBitString \ + asnObjectIdentifer + + # Decoder commands + namespace export \ + asnGetResponse \ + asnGetInteger \ + asnGetEnumeration \ + asnGetOctetString \ + asnGetSequence \ + asnGetSet \ + asnGetApplication \ + asnGetNumericString \ + asnGetPrintableString \ + asnGetIA5String \ + asnGetBMPString \ + asnGetUTF8String \ + asnGetObjectIdentifier \ + asnGetBoolean \ + asnGetUTCTime \ + asnGetBitString \ + asnGetContext + + # general BER utility commands + namespace export \ + asnPeekByte \ + asnGetLength \ + asnRetag \ + asnPeekTag \ + asnTag + +} + +#----------------------------------------------------------------------------- +# Implementation notes: +# +# See the 'asn_ber_intro.txt' in this directory for an introduction +# into BER/DER encoding of ASN.1 information. Bibliography information +# +# A Layman's Guide to a Subset of ASN.1, BER, and DER +# +# An RSA Laboratories Technical Note +# Burton S. Kaliski Jr. +# Revised November 1, 1993 +# +# Supersedes June 3, 1991 version, which was also published as +# NIST/OSI Implementors' Workshop document SEC-SIG-91-17. +# PKCS documents are available by electronic mail to +# <pkcs@rsa.com>. +# +# Copyright (C) 1991-1993 RSA Laboratories, a division of RSA +# Data Security, Inc. License to copy this document is granted +# provided that it is identified as "RSA Data Security, Inc. +# Public-Key Cryptography Standards (PKCS)" in all material +# mentioning or referencing this document. +# 003-903015-110-000-000 +# +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# asnLength : Encode some length data. Helper command. +#----------------------------------------------------------------------------- + +proc ::asn::asnLength {len} { + + if {$len < 0} { + return -code error "Negative length octet requested" + } + if {$len < 128} { + # short form: ISO X.690 8.1.3.4 + return [binary format c $len] + } + # long form: ISO X.690 8.1.3.5 + # try to use a minimal encoding, + # even if not required by BER, but it is required by DER + # take care for signed vs. unsigned issues + if {$len < 256 } { + return [binary format H2c 81 [expr {$len - 256}]] + } + if {$len < 32769} { + # two octet signed value + return [binary format H2S 82 $len] + } + if {$len < 65536} { + return [binary format H2S 82 [expr {$len - 65536}]] + } + if {$len < 8388608} { + # three octet signed value + return [binary format H2cS 83 [expr {$len >> 16}] [expr {($len & 0xFFFF) - 65536}]] + } + if {$len < 16777216} { + # three octet signed value + return [binary format H2cS 83 [expr {($len >> 16) -256}] [expr {($len & 0xFFFF) -65536}]] + } + if {$len < 2147483649} { + # four octet signed value + return [binary format H2I 84 $len] + } + if {$len < 4294967296} { + # four octet unsigned value + return [binary format H2I 84 [expr {$len - 4294967296}]] + } + if {$len < 1099511627776} { + # five octet unsigned value + return [binary format H2 85][string range [binary format W $len] 3 end] + } + if {$len < 281474976710656} { + # six octet unsigned value + return [binary format H2 86][string range [binary format W $len] 2 end] + } + if {$len < 72057594037927936} { + # seven octet value + return [binary format H2 87][string range [binary format W $len] 1 end] + } + + # must be a 64-bit wide signed value + return [binary format H2W 88 $len] +} + +#----------------------------------------------------------------------------- +# asnSequence : Assumes that the arguments are already ASN encoded. +#----------------------------------------------------------------------------- + +proc ::asn::asnSequence {args} { + asnSequenceFromList $args +} + +proc ::asn::asnSequenceFromList {lst} { + # The sequence tag is 0x30. The length is arbitrary and thus full + # length coding is required. The arguments have to be BER encoded + # already. Constructed value, definite-length encoding. + + set out "" + foreach part $lst { + append out $part + } + set len [string length $out] + return [binary format H2a*a$len 30 [asnLength $len] $out] +} + + +#----------------------------------------------------------------------------- +# asnSet : Assumes that the arguments are already ASN encoded. +#----------------------------------------------------------------------------- + +proc ::asn::asnSet {args} { + asnSetFromList $args +} + +proc ::asn::asnSetFromList {lst} { + # The set tag is 0x31. The length is arbitrary and thus full + # length coding is required. The arguments have to be BER encoded + # already. + + set out "" + foreach part $lst { + append out $part + } + set len [string length $out] + return [binary format H2a*a$len 31 [asnLength $len] $out] +} + + +#----------------------------------------------------------------------------- +# asnApplicationConstr +#----------------------------------------------------------------------------- + +proc ::asn::asnApplicationConstr {appNumber args} { + # Packs the arguments into a constructed value with application tag. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x060 + $appNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnApplication +#----------------------------------------------------------------------------- + +proc ::asn::asnApplication {appNumber data} { + # Packs the arguments into a constructed value with application tag. + + set code [expr {0x040 + $appNumber}] + set len [string length $data] + return [binary format ca*a$len $code [asnLength $len] $data] +} + +#----------------------------------------------------------------------------- +# asnContextConstr +#----------------------------------------------------------------------------- + +proc ::asn::asnContextConstr {contextNumber args} { + # Packs the arguments into a constructed value with application tag. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x0A0 + $contextNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnContext +#----------------------------------------------------------------------------- + +proc ::asn::asnContext {contextNumber data} { + # Packs the arguments into a constructed value with application tag. + set code [expr {0x080 + $contextNumber}] + set len [string length $data] + return [binary format ca*a$len $code [asnLength $len] $data] +} +#----------------------------------------------------------------------------- +# asnChoice +#----------------------------------------------------------------------------- + +proc ::asn::asnChoice {appNumber args} { + # Packs the arguments into a choice construction. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x080 + $appNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnChoiceConstr +#----------------------------------------------------------------------------- + +proc ::asn::asnChoiceConstr {appNumber args} { + # Packs the arguments into a choice construction. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x0A0 + $appNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnInteger : Encode integer value. +#----------------------------------------------------------------------------- + +proc ::asn::asnInteger {number} { + asnIntegerOrEnum 02 $number +} + +#----------------------------------------------------------------------------- +# asnEnumeration : Encode enumeration value. +#----------------------------------------------------------------------------- + +proc ::asn::asnEnumeration {number} { + asnIntegerOrEnum 0a $number +} + +#----------------------------------------------------------------------------- +# asnIntegerOrEnum : Common code for Integers and Enumerations +# No Bignum version, as we do not expect large Enums. +#----------------------------------------------------------------------------- + +proc ::asn::asnIntegerOrEnum {tag number} { + # The integer tag is 0x02 , the Enum Tag 0x0a otherwise identical. + # The length is 1, 2, 3, or 4, coded in a + # single byte. This can be done directly, no need to go through + # asnLength. The value itself is written in big-endian. + + # Known bug/issue: The command cannot handle very wide integers, i.e. + # anything above 8 bytes length. Use asnBignumInteger for those. + + # check if we really have an int + set num $number + incr num + + if {($number >= -128) && ($number < 128)} { + return [binary format H2H2c $tag 01 $number] + } + if {($number >= -32768) && ($number < 32768)} { + return [binary format H2H2S $tag 02 $number] + } + if {($number >= -8388608) && ($number < 8388608)} { + set numberb [expr {$number & 0xFFFF}] + set numbera [expr {($number >> 16) & 0xFF}] + return [binary format H2H2cS $tag 03 $numbera $numberb] + } + if {($number >= -2147483648) && ($number < 2147483648)} { + return [binary format H2H2I $tag 04 $number] + } + if {($number >= -549755813888) && ($number < 549755813888)} { + set numberb [expr {$number & 0xFFFFFFFF}] + set numbera [expr {($number >> 32) & 0xFF}] + return [binary format H2H2cI $tag 05 $numbera $numberb] + } + if {($number >= -140737488355328) && ($number < 140737488355328)} { + set numberb [expr {$number & 0xFFFFFFFF}] + set numbera [expr {($number >> 32) & 0xFFFF}] + return [binary format H2H2SI $tag 06 $numbera $numberb] + } + if {($number >= -36028797018963968) && ($number < 36028797018963968)} { + set numberc [expr {$number & 0xFFFFFFFF}] + set numberb [expr {($number >> 32) & 0xFFFF}] + set numbera [expr {($number >> 48) & 0xFF}] + return [binary format H2H2cSI $tag 07 $numbera $numberb $numberc] + } + if {($number >= -9223372036854775808) && ($number <= 9223372036854775807)} { + return [binary format H2H2W $tag 08 $number] + } + return -code error "Integer value to large to encode, use asnBigInteger" +} + +#----------------------------------------------------------------------------- +# asnBigInteger : Encode a long integer value using math::bignum +#----------------------------------------------------------------------------- + +proc ::asn::asnBigInteger {bignum} { + # require math::bignum only if it is used + package require math::bignum + + # this is a hack to check for bignum... + if {[llength $bignum] < 2 || ([lindex $bignum 0] ne "bignum")} { + return -code error "expected math::bignum value got \"$bignum\"" + } + if {[math::bignum::sign $bignum]} { + # generate two's complement form + set bits [math::bignum::bits $bignum] + set padding [expr {$bits % 8}] + set len [expr {int(ceil($bits / 8.0))}] + if {$padding == 0} { + # we need a complete extra byte for the sign + # unless this is a base 2 multiple + set test [math::bignum::fromstr 0] + math::bignum::setbit test [expr {$bits-1}] + if {[math::bignum::ne [math::bignum::abs $bignum] $test]} { + incr len + } + } + set exp [math::bignum::pow \ + [math::bignum::fromstr 256] \ + [math::bignum::fromstr $len]] + set bignum [math::bignum::add $bignum $exp] + set hex [math::bignum::tostr $bignum 16] + } else { + set bits [math::bignum::bits $bignum] + if {($bits % 8) == 0 && $bits > 0} { + set pad "00" + } else { + set pad "" + } + set hex $pad[math::bignum::tostr $bignum 16] + } + if {[string length $hex]%2} { + set hex "0$hex" + } + set octets [expr {(([string length $hex]+1)/2)}] + return [binary format H2a*H* 02 [asnLength $octets] $hex] +} + + +#----------------------------------------------------------------------------- +# asnBoolean : Encode a boolean value. +#----------------------------------------------------------------------------- + +proc ::asn::asnBoolean {bool} { + # The boolean tag is 0x01. The length is always 1, coded in + # a single byte. This can be done directly, no need to go through + # asnLength. The value itself is written in big-endian. + + return [binary format H2H2c 01 01 [expr {$bool ? 0x0FF : 0x0}]] +} + +#----------------------------------------------------------------------------- +# asnOctetString : Encode a string of arbitrary bytes +#----------------------------------------------------------------------------- + +proc ::asn::asnOctetString {string} { + # The octet tag is 0x04. The length is arbitrary, so we need + # 'asnLength' for full coding of the length. + + set len [string length $string] + return [binary format H2a*a$len 04 [asnLength $len] $string] +} + +#----------------------------------------------------------------------------- +# asnNull : Encode a null value +#----------------------------------------------------------------------------- + +proc ::asn::asnNull {} { + # Null has only one valid encoding + return \x05\x00 +} + +#----------------------------------------------------------------------------- +# asnBitstring : Encode a Bit String value +#----------------------------------------------------------------------------- + +proc ::asn::asnBitString {bitstring} { + # The bit string tag is 0x03. + # Bit strings can be either simple or constructed + # we always use simple encoding + + set bitlen [string length $bitstring] + set padding [expr {(8 - ($bitlen % 8)) % 8}] + set len [expr {($bitlen / 8) + 1}] + if {$padding != 0} { incr len } + + return [binary format H2a*cB* 03 [asnLength $len] $padding $bitstring] +} + +#----------------------------------------------------------------------------- +# asnUTCTime : Encode an UTC time string +#----------------------------------------------------------------------------- + +proc ::asn::asnUTCTime {UTCtimestring} { + # the utc time tag is 0x17. + # + # BUG: we do not check the string for well formedness + + set ascii [encoding convertto ascii $UTCtimestring] + set len [string length $ascii] + return [binary format H2a*a* 17 [asnLength $len] $ascii] +} + +#----------------------------------------------------------------------------- +# asnPrintableString : Encode a printable string +#----------------------------------------------------------------------------- +namespace eval asn { + variable nonPrintableChars {[^ A-Za-z0-9'()+,.:/?=-]} +} +proc ::asn::asnPrintableString {string} { + # the printable string tag is 0x13 + variable nonPrintableChars + # it is basically a restricted ascii string + if {[regexp $nonPrintableChars $string ]} { + return -code error "Illegal character in PrintableString." + } + + # check characters + set ascii [encoding convertto ascii $string] + return [asnEncodeString 13 $ascii] +} + +#----------------------------------------------------------------------------- +# asnIA5String : Encode an Ascii String +#----------------------------------------------------------------------------- +proc ::asn::asnIA5String {string} { + # the IA5 string tag is 0x16 + # check for extended charachers + if {[string length $string]!=[string bytelength $string]} { + return -code error "Illegal character in IA5String" + } + set ascii [encoding convertto ascii $string] + return [asnEncodeString 16 $ascii] +} + +#----------------------------------------------------------------------------- +# asnNumericString : Encode a Numeric String type +#----------------------------------------------------------------------------- +namespace eval asn { + variable nonNumericChars {[^0-9 ]} +} +proc ::asn::asnNumericString {string} { + # the Numeric String type has tag 0x12 + variable nonNumericChars + if {[regexp $nonNumericChars $string]} { + return -code error "Illegal character in Numeric String." + } + + return [asnEncodeString 12 $string] +} +#---------------------------------------------------------------------- +# asnBMPString: Encode a Tcl string as Basic Multinligval (UCS2) string +#----------------------------------------------------------------------- +proc asn::asnBMPString {string} { + if {$::tcl_platform(byteOrder) eq "littleEndian"} { + set bytes "" + foreach {lo hi} [split [encoding convertto unicode $string] ""] { + append bytes $hi $lo + } + } else { + set bytes [encoding convertto unicode $string] + } + return [asnEncodeString 1e $bytes] +} +#--------------------------------------------------------------------------- +# asnUTF8String: encode tcl string as UTF8 String +#---------------------------------------------------------------------------- +proc asn::asnUTF8String {string} { + return [asnEncodeString 0c [encoding convertto utf-8 $string]] +} +#----------------------------------------------------------------------------- +# asnEncodeString : Encode an RestrictedCharacter String +#----------------------------------------------------------------------------- +proc ::asn::asnEncodeString {tag string} { + set len [string length $string] + return [binary format H2a*a$len $tag [asnLength $len] $string] +} + +#----------------------------------------------------------------------------- +# asnObjectIdentifier : Encode an Object Identifier value +#----------------------------------------------------------------------------- +proc ::asn::asnObjectIdentifier {oid} { + # the object identifier tag is 0x06 + + if {[llength $oid] < 2} { + return -code error "OID must have at least two subidentifiers." + } + + # basic check that it is valid + foreach identifier $oid { + if {$identifier < 0} { + return -code error \ + "Malformed OID. Identifiers must be positive Integers." + } + } + + if {[lindex $oid 0] > 2} { + return -code error "First subidentifier must be 0,1 or 2" + } + if {[lindex $oid 1] > 39} { + return -code error \ + "Second subidentifier must be between 0 and 39" + } + + # handle the special cases directly + switch [llength $oid] { + 2 { return [binary format H2H2c 06 01 \ + [expr {[lindex $oid 0]*40+[lindex $oid 1]}]] } + default { + # This can probably be written much shorter. + # Just a first try that works... + # + set octets [binary format c \ + [expr {[lindex $oid 0]*40+[lindex $oid 1]}]] + foreach identifier [lrange $oid 2 end] { + set d 128 + if {$identifier < 128} { + set subidentifier [list $identifier] + } else { + set subidentifier [list] + # find the largest divisor + + while {($identifier / $d) >= 128} { + set d [expr {$d * 128}] + } + # and construct the subidentifiers + set remainder $identifier + while {$d >= 128} { + set coefficient [expr {($remainder / $d) | 0x80}] + set remainder [expr {$remainder % $d}] + set d [expr {$d / 128}] + lappend subidentifier $coefficient + } + lappend subidentifier $remainder + } + append octets [binary format c* $subidentifier] + } + return [binary format H2a*a* 06 \ + [asnLength [string length $octets]] $octets] + } + } + +} + +#----------------------------------------------------------------------------- +# asnGetResponse : Read a ASN response from a channel. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetResponse {sock data_var} { + upvar 1 $data_var data + + # We expect a sequence here (tag 0x30). The code below is an + # inlined replica of 'asnGetSequence', modified for reading from a + # channel instead of a string. + + set tag [read $sock 1] + + if {$tag == "\x30"} { + # The following code is a replica of 'asnGetLength', modified + # for reading the bytes from the channel instead of a string. + + set len1 [read $sock 1] + binary scan $len1 c num + set length [expr {($num + 0x100) % 0x100}] + + if {$length >= 0x080} { + # The byte the read is not the length, but a prefix, and + # the lower nibble tells us how many bytes follow. + + set len_length [expr {$length & 0x7f}] + + # BUG: We should not perform the value extraction for an + # BUG: improper length. It wastes cycles, and here it can + # BUG: cause us trouble, reading more data than there is + # BUG: on the channel. Depending on the channel + # BUG: configuration an attacker can induce us to block, + # BUG: causing a denial of service. + set lengthBytes [read $sock $len_length] + + switch $len_length { + 1 { + binary scan $lengthBytes c length + set length [expr {($length + 0x100) % 0x100}] + } + 2 { binary scan $lengthBytes S length } + 3 { binary scan \x00$lengthBytes I length } + 4 { binary scan $lengthBytes I length } + default { + return -code error \ + "length information too long ($len_length)" + } + } + } + + # Now that the length is known we get the remainder, + # i.e. payload, and construct proper in-memory BER encoded + # sequence. + + set rest [read $sock $length] + set data [binary format aa*a$length $tag [asnLength $length] $rest] + } else { + # Generate an error message if the data is not a sequence as + # we expected. + + set tag_hex "" + binary scan $tag H2 tag_hex + return -code error "unknown start tag [string length $tag] $tag_hex" + } +} + +if {[package vsatisfies [package present Tcl] 8.5.0]} { +############################################################################## +# Code for 8.5 +############################################################################## +#----------------------------------------------------------------------------- +# asnGetByte (8.5 version) : Retrieve a single byte from the data (unsigned) +#----------------------------------------------------------------------------- + +proc ::asn::asnGetByte {data_var byte_var} { + upvar 1 $data_var data $byte_var byte + + binary scan [string index $data 0] cu byte + set data [string range $data 1 end] + + return +} + +#----------------------------------------------------------------------------- +# asnPeekByte (8.5 version) : Retrieve a single byte from the data (unsigned) +# without removing it. +#----------------------------------------------------------------------------- + +proc ::asn::asnPeekByte {data_var byte_var {offset 0}} { + upvar 1 $data_var data $byte_var byte + + binary scan [string index $data $offset] cu byte + + return +} + +#----------------------------------------------------------------------------- +# asnGetLength (8.5 version) : Decode an ASN length value (See notes) +#----------------------------------------------------------------------------- + +proc ::asn::asnGetLength {data_var length_var} { + upvar 1 $data_var data $length_var length + + asnGetByte data length + if {$length == 0x080} { + return -code error "Indefinite length BER encoding not yet supported" + } + if {$length > 0x080} { + # The retrieved byte is a prefix value, and the integer in the + # lower nibble tells us how many bytes were used to encode the + # length data following immediately after this prefix. + + set len_length [expr {$length & 0x7f}] + + if {[string length $data] < $len_length} { + return -code error \ + "length information invalid, not enough octets left" + } + + asnGetBytes data $len_length lengthBytes + + switch $len_length { + 1 { binary scan $lengthBytes cu length } + 2 { binary scan $lengthBytes Su length } + 3 { binary scan \x00$lengthBytes Iu length } + 4 { binary scan $lengthBytes Iu length } + default { + binary scan $lengthBytes H* hexstr + scan $hexstr %llx length + } + } + } + return +} + +} else { +############################################################################## +# Code for Tcl 8.4 +############################################################################## +#----------------------------------------------------------------------------- +# asnGetByte : Retrieve a single byte from the data (unsigned) +#----------------------------------------------------------------------------- + +proc ::asn::asnGetByte {data_var byte_var} { + upvar 1 $data_var data $byte_var byte + + binary scan [string index $data 0] c byte + set byte [expr {($byte + 0x100) % 0x100}] + set data [string range $data 1 end] + + return +} + +#----------------------------------------------------------------------------- +# asnPeekByte : Retrieve a single byte from the data (unsigned) +# without removing it. +#----------------------------------------------------------------------------- + +proc ::asn::asnPeekByte {data_var byte_var {offset 0}} { + upvar 1 $data_var data $byte_var byte + + binary scan [string index $data $offset] c byte + set byte [expr {($byte + 0x100) % 0x100}] + + return +} + +#----------------------------------------------------------------------------- +# asnGetLength : Decode an ASN length value (See notes) +#----------------------------------------------------------------------------- + +proc ::asn::asnGetLength {data_var length_var} { + upvar 1 $data_var data $length_var length + + asnGetByte data length + if {$length == 0x080} { + return -code error "Indefinite length BER encoding not yet supported" + } + if {$length > 0x080} { + # The retrieved byte is a prefix value, and the integer in the + # lower nibble tells us how many bytes were used to encode the + # length data following immediately after this prefix. + + set len_length [expr {$length & 0x7f}] + + if {[string length $data] < $len_length} { + return -code error \ + "length information invalid, not enough octets left" + } + + asnGetBytes data $len_length lengthBytes + + switch $len_length { + 1 { + # Efficiently coded data will not go through this + # path, as small length values can be coded directly, + # without a prefix. + + binary scan $lengthBytes c length + set length [expr {($length + 0x100) % 0x100}] + } + 2 { binary scan $lengthBytes S length + set length [expr {($length + 0x10000) % 0x10000}] + } + 3 { binary scan \x00$lengthBytes I length + set length [expr {($length + 0x1000000) % 0x1000000}] + } + 4 { binary scan $lengthBytes I length + set length [expr {(wide($length) + 0x100000000) % 0x100000000}] + } + default { + binary scan $lengthBytes H* hexstr + # skip leading zeros which are allowed by BER + set hexlen [string trimleft $hexstr 0] + # check if it fits into a 64-bit signed integer + if {[string length $hexlen] > 16} { + return -code error -errorcode {ARITH IOVERFLOW + {Length value too large for normal use, try asnGetBigLength}} \ + "Length value to large" + } elseif { [string length $hexlen] == 16 \ + && ([string index $hexlen 0] & 0x8)} { + # check most significant bit, if set we need bignum + return -code error -errorcode {ARITH IOVERFLOW + {Length value too large for normal use, try asnGetBigLength}} \ + "Length value to large" + } else { + scan $hexstr "%lx" length + } + } + } + } + return +} + +} + +#----------------------------------------------------------------------------- +# asnRetag: Remove an explicit tag with the real newTag +# +#----------------------------------------------------------------------------- +proc ::asn::asnRetag {data_var newTag} { + upvar 1 $data_var data + set tag "" + set type "" + set len [asnPeekTag data tag type dummy] + asnGetBytes data $len tagbytes + set data [binary format c* $newTag]$data +} + +#----------------------------------------------------------------------------- +# asnGetBytes : Retrieve a block of 'length' bytes from the data. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetBytes {data_var length bytes_var} { + upvar 1 $data_var data $bytes_var bytes + + incr length -1 + set bytes [string range $data 0 $length] + incr length + set data [string range $data $length end] + + return +} + +#----------------------------------------------------------------------------- +# asnPeekTag : Decode the tag value +#----------------------------------------------------------------------------- + +proc ::asn::asnPeekTag {data_var tag_var tagtype_var constr_var} { + upvar 1 $data_var data $tag_var tag $tagtype_var tagtype $constr_var constr + + set type 0 + set offset 0 + asnPeekByte data type $offset + # check if we have a simple tag, < 31, which fits in one byte + + set tval [expr {$type & 0x1f}] + if {$tval == 0x1f} { + # long tag, max 64-bit with Tcl 8.4, unlimited with 8.5 bignum + asnPeekByte data tagbyte [incr offset] + set tval [expr {wide($tagbyte & 0x7f)}] + while {($tagbyte & 0x80)} { + asnPeekByte data tagbyte [incr offset] + set tval [expr {($tval << 7) + ($tagbyte & 0x7f)}] + } + } + + set tagtype [lindex {UNIVERSAL APPLICATION CONTEXT PRIVATE} \ + [expr {($type & 0xc0) >>6}]] + set tag $tval + set constr [expr {($type & 0x20) > 0}] + + return [incr offset] +} + +#----------------------------------------------------------------------------- +# asnTag : Build a tag value +#----------------------------------------------------------------------------- + +proc ::asn::asnTag {tagnumber {class UNIVERSAL} {tagstyle P}} { + set first 0 + if {$tagnumber < 31} { + # encode everything in one byte + set first $tagnumber + set bytes [list] + } else { + # multi-byte tag + set first 31 + set bytes [list [expr {$tagnumber & 0x7f}]] + set tagnumber [expr {$tagnumber >> 7}] + while {$tagnumber > 0} { + lappend bytes [expr {($tagnumber & 0x7f)+0x80}] + set tagnumber [expr {$tagnumber >>7}] + } + + } + + if {$tagstyle eq "C" || $tagstyle == 1 } {incr first 32} + switch -glob -- $class { + U* { ;# UNIVERSAL } + A* { incr first 64 ;# APPLICATION } + C* { incr first 128 ;# CONTEXT } + P* { incr first 192 ;# PRIVATE } + default { + return -code error "Unknown tag class \"$class\"" + } + } + if {[llength $bytes] > 0} { + # long tag + set rbytes [list] + for {set i [expr {[llength $bytes]-1}]} {$i >= 0} {incr i -1} { + lappend rbytes [lindex $bytes $i] + } + return [binary format cc* $first $rbytes ] + } + return [binary format c $first] +} + + + +#----------------------------------------------------------------------------- +# asnGetBigLength : Retrieve a length that can not be represented in 63-bit +#----------------------------------------------------------------------------- + +proc ::asn::asnGetBigLength {data_var biglength_var} { + + # Does any real world code really need this? + # If we encounter this, we are doomed to fail anyway, + # (there would be an Exabyte inside the data_var, ) + # + # So i implement it just for completeness. + # + package require math::bignum + + upvar 1 $data_var data $biglength_var length + + asnGetByte data length + if {$length == 0x080} { + return -code error "Indefinite length BER encoding not yet supported" + } + if {$length > 0x080} { + # The retrieved byte is a prefix value, and the integer in the + # lower nibble tells us how many bytes were used to encode the + # length data following immediately after this prefix. + + set len_length [expr {$length & 0x7f}] + + if {[string length $data] < $len_length} { + return -code error \ + "length information invalid, not enough octets left" + } + + asnGetBytes data $len_length lengthBytes + binary scan $lengthBytes H* hexlen + set length [math::bignum::fromstr $hexlen 16] + } + return +} + +#----------------------------------------------------------------------------- +# asnGetInteger : Retrieve integer. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetInteger {data_var int_var} { + # Tag is 0x02. + + upvar 1 $data_var data $int_var int + + asnGetByte data tag + + if {$tag != 0x02} { + return -code error \ + [format "Expected Integer (0x02), but got %02x" $tag] + } + + asnGetLength data len + asnGetBytes data $len integerBytes + + set int ? + + switch $len { + 1 { binary scan $integerBytes c int } + 2 { binary scan $integerBytes S int } + 3 { + # check for negative int and pad + scan [string index $integerBytes 0] %c byte + if {$byte & 128} { + binary scan \xff$integerBytes I int + } else { + binary scan \x00$integerBytes I int + } + } + 4 { binary scan $integerBytes I int } + 5 - + 6 - + 7 - + 8 { + # check for negative int and pad + scan [string index $integerBytes 0] %c byte + if {$byte & 128} { + set pad [string repeat \xff [expr {8-$len}]] + } else { + set pad [string repeat \x00 [expr {8-$len}]] + } + binary scan $pad$integerBytes W int + } + default { + # Too long, or prefix coding was used. + return -code error "length information too long" + } + } + return +} + +#----------------------------------------------------------------------------- +# asnGetBigInteger : Retrieve a big integer. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetBigInteger {data_var bignum_var} { + # require math::bignum only if it is used + package require math::bignum + + # Tag is 0x02. We expect that the length of the integer is coded with + # maximal efficiency, i.e. without a prefix 0x81 prefix. If a prefix + # is used this decoder will fail. + + upvar $data_var data $bignum_var bignum + + asnGetByte data tag + + if {$tag != 0x02} { + return -code error \ + [format "Expected Integer (0x02), but got %02x" $tag] + } + + asnGetLength data len + asnGetBytes data $len integerBytes + + binary scan [string index $integerBytes 0] H* hex_head + set head [expr 0x$hex_head] + set replacement_head [expr {$head & 0x7f}] + set integerBytes [string replace $integerBytes 0 0 [format %c $replacement_head]] + + binary scan $integerBytes H* hex + + set bignum [math::bignum::fromstr $hex 16] + + if {($head >> 7) && 1} { + set bigsub [math::bignum::pow [::math::bignum::fromstr 2] [::math::bignum::fromstr [expr {($len * 8) - 1}]]] + set bignum [math::bignum::sub $bignum $bigsub] + } + + return $bignum +} + + + + +#----------------------------------------------------------------------------- +# asnGetEnumeration : Retrieve an enumeration id +#----------------------------------------------------------------------------- + +proc ::asn::asnGetEnumeration {data_var enum_var} { + # This is like 'asnGetInteger', except for a different tag. + + upvar 1 $data_var data $enum_var enum + + asnGetByte data tag + + if {$tag != 0x0a} { + return -code error \ + [format "Expected Enumeration (0x0a), but got %02x" $tag] + } + + asnGetLength data len + asnGetBytes data $len integerBytes + set enum ? + + switch $len { + 1 { binary scan $integerBytes c enum } + 2 { binary scan $integerBytes S enum } + 3 { binary scan \x00$integerBytes I enum } + 4 { binary scan $integerBytes I enum } + default { + return -code error "length information too long" + } + } + return +} + +#----------------------------------------------------------------------------- +# asnGetOctetString : Retrieve arbitrary string. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetOctetString {data_var string_var} { + # Here we need the full decoder for length data. + + upvar 1 $data_var data $string_var string + + asnGetByte data tag + if {$tag != 0x04} { + return -code error \ + [format "Expected Octet String (0x04), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length temp + set string $temp + return +} + +#----------------------------------------------------------------------------- +# asnGetSequence : Retrieve Sequence data for further decoding. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetSequence {data_var sequence_var} { + # Here we need the full decoder for length data. + + upvar 1 $data_var data $sequence_var sequence + + asnGetByte data tag + if {$tag != 0x030} { + return -code error \ + [format "Expected Sequence (0x30), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length temp + set sequence $temp + return +} + +#----------------------------------------------------------------------------- +# asnGetSet : Retrieve Set data for further decoding. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetSet {data_var set_var} { + # Here we need the full decoder for length data. + + upvar 1 $data_var data $set_var set + + asnGetByte data tag + if {$tag != 0x031} { + return -code error \ + [format "Expected Set (0x31), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length temp + set set $temp + return +} + +#----------------------------------------------------------------------------- +# asnGetApplication +#----------------------------------------------------------------------------- + +proc ::asn::asnGetApplication {data_var appNumber_var {content_var {}} {encodingType_var {}} } { + upvar 1 $data_var data $appNumber_var appNumber + + asnGetByte data tag + asnGetLength data length + + if {($tag & 0xC0) != 0x40} { + return -code error \ + [format "Expected Application, but got %02x" $tag] + } + if {$encodingType_var != {}} { + upvar 1 $encodingType_var encodingType + set encodingType [expr {($tag & 0x20) > 0}] + } + set appNumber [expr {$tag & 0x1F}] + if {[string length $content_var]} { + upvar 1 $content_var content + asnGetBytes data $length content + } + return +} + +#----------------------------------------------------------------------------- +# asnGetBoolean: decode a boolean value +#----------------------------------------------------------------------------- + +proc asn::asnGetBoolean {data_var bool_var} { + upvar 1 $data_var data $bool_var bool + + asnGetByte data tag + if {$tag != 0x01} { + return -code error \ + [format "Expected Boolean (0x01), but got %02x" $tag] + } + + asnGetLength data length + asnGetByte data byte + set bool [expr {$byte == 0 ? 0 : 1}] + return +} + +#----------------------------------------------------------------------------- +# asnGetUTCTime: Extract an UTC Time string from the data. Returns a string +# representing an UTC Time. +# +#----------------------------------------------------------------------------- + +proc asn::asnGetUTCTime {data_var utc_var} { + upvar 1 $data_var data $utc_var utc + + asnGetByte data tag + if {$tag != 0x17} { + return -code error \ + [format "Expected UTCTime (0x17), but got %02x" $tag] + } + + asnGetLength data length + asnGetBytes data $length bytes + + # this should be ascii, make it explicit + set bytes [encoding convertfrom ascii $bytes] + binary scan $bytes a* utc + + return +} + + +#----------------------------------------------------------------------------- +# asnGetBitString: Extract a Bit String value (a string of 0/1s) from the +# ASN.1 data. +# +#----------------------------------------------------------------------------- + +proc asn::asnGetBitString {data_var bitstring_var} { + upvar 1 $data_var data $bitstring_var bitstring + + asnGetByte data tag + if {$tag != 0x03} { + return -code error \ + [format "Expected Bit String (0x03), but got %02x" $tag] + } + + asnGetLength data length + # get the number of padding bits used at the end + asnGetByte data padding + incr length -1 + asnGetBytes data $length bytes + binary scan $bytes B* bits + + # cut off the padding bits + set bits [string range $bits 0 end-$padding] + set bitstring $bits +} + +#----------------------------------------------------------------------------- +# asnGetObjectIdentifier: Decode an ASN.1 Object Identifier (OID) into +# a Tcl list of integers. +#----------------------------------------------------------------------------- + +proc asn::asnGetObjectIdentifier {data_var oid_var} { + upvar 1 $data_var data $oid_var oid + + asnGetByte data tag + if {$tag != 0x06} { + return -code error \ + [format "Expected Object Identifier (0x06), but got %02x" $tag] + } + asnGetLength data length + + # the first byte encodes the OID parts in position 0 and 1 + asnGetByte data val + set oid [expr {$val / 40}] + lappend oid [expr {$val % 40}] + incr length -1 + + # the next bytes encode the remaining parts of the OID + set bytes [list] + set incomplete 0 + while {$length} { + asnGetByte data octet + incr length -1 + if {$octet < 128} { + set oidval $octet + set mult 128 + foreach byte $bytes { + if {$byte != {}} { + incr oidval [expr {$mult*$byte}] + set mult [expr {$mult*128}] + } + } + lappend oid $oidval + set bytes [list] + set incomplete 0 + } else { + set byte [expr {$octet-128}] + set bytes [concat [list $byte] $bytes] + set incomplete 1 + } + } + if {$incomplete} { + return -code error "OID Data is incomplete, not enough octets." + } + return +} + +#----------------------------------------------------------------------------- +# asnGetContext: Decode an explicit context tag +# +#----------------------------------------------------------------------------- + +proc ::asn::asnGetContext {data_var contextNumber_var {content_var {}} {encodingType_var {}}} { + upvar 1 $data_var data $contextNumber_var contextNumber + + asnGetByte data tag + asnGetLength data length + + if {($tag & 0xC0) != 0x80} { + return -code error \ + [format "Expected Context, but got %02x" $tag] + } + if {$encodingType_var != {}} { + upvar 1 $encodingType_var encodingType + set encodingType [expr {($tag & 0x20) > 0}] + } + set contextNumber [expr {$tag & 0x1F}] + if {[string length $content_var]} { + upvar 1 $content_var content + asnGetBytes data $length content + } + return +} + + +#----------------------------------------------------------------------------- +# asnGetNumericString: Decode a Numeric String from the data +#----------------------------------------------------------------------------- + +proc ::asn::asnGetNumericString {data_var print_var} { + upvar 1 $data_var data $print_var print + + asnGetByte data tag + if {$tag != 0x12} { + return -code error \ + [format "Expected Numeric String (0x12), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + set print [encoding convertfrom ascii $string] + return +} + +#----------------------------------------------------------------------------- +# asnGetPrintableString: Decode a Printable String from the data +#----------------------------------------------------------------------------- + +proc ::asn::asnGetPrintableString {data_var print_var} { + upvar 1 $data_var data $print_var print + + asnGetByte data tag + if {$tag != 0x13} { + return -code error \ + [format "Expected Printable String (0x13), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + set print [encoding convertfrom ascii $string] + return +} + +#----------------------------------------------------------------------------- +# asnGetIA5String: Decode a IA5(ASCII) String from the data +#----------------------------------------------------------------------------- + +proc ::asn::asnGetIA5String {data_var print_var} { + upvar 1 $data_var data $print_var print + + asnGetByte data tag + if {$tag != 0x16} { + return -code error \ + [format "Expected IA5 String (0x16), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + set print [encoding convertfrom ascii $string] + return +} +#------------------------------------------------------------------------ +# asnGetBMPString: Decode Basic Multiningval (UCS2 string) from data +#------------------------------------------------------------------------ +proc asn::asnGetBMPString {data_var print_var} { + upvar 1 $data_var data $print_var print + asnGetByte data tag + if {$tag != 0x1e} { + return -code error \ + [format "Expected BMP String (0x1e), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + if {$::tcl_platform(byteOrder) eq "littleEndian"} { + set str2 "" + foreach {hi lo} [split $string ""] { + append str2 $lo $hi + } + } else { + set str2 $string + } + set print [encoding convertfrom unicode $str2] + return +} +#------------------------------------------------------------------------ +# asnGetUTF8String: Decode UTF8 string from data +#------------------------------------------------------------------------ +proc asn::asnGetUTF8String {data_var print_var} { + upvar 1 $data_var data $print_var print + asnGetByte data tag + if {$tag != 0x0c} { + return -code error \ + [format "Expected UTF8 String (0x0c), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + #there should be some error checking to see if input is + #properly-formatted utf8 + set print [encoding convertfrom utf-8 $string] + + return +} +#----------------------------------------------------------------------------- +# asnGetNull: decode a NULL value +#----------------------------------------------------------------------------- + +proc ::asn::asnGetNull {data_var} { + upvar 1 $data_var data + + asnGetByte data tag + if {$tag != 0x05} { + return -code error \ + [format "Expected NULL (0x05), but got %02x" $tag] + } + + asnGetLength data length + asnGetBytes data $length bytes + + # we do not check the null data, all bytes must be 0x00 + + return +} + +#---------------------------------------------------------------------------- +# MultiType string routines +#---------------------------------------------------------------------------- + +namespace eval asn { + variable stringTypes + array set stringTypes { + 12 NumericString + 13 PrintableString + 16 IA5String + 1e BMPString + 0c UTF8String + 14 T61String + 15 VideotexString + 1a VisibleString + 1b GeneralString + 1c UniversalString + } + variable defaultStringType UTF8 +} +#--------------------------------------------------------------------------- +# asnGetString - get readable string automatically detecting its type +#--------------------------------------------------------------------------- +proc ::asn::asnGetString {data_var print_var {type_var {}}} { + variable stringTypes + upvar 1 $data_var data $print_var print + asnPeekByte data tag + set tag [format %02x $tag] + if {![info exists stringTypes($tag)]} { + return -code error "Expected one of string types, but got $tag" + } + asnGet$stringTypes($tag) data print + if {[string length $type_var]} { + upvar $type_var type + set type $stringTypes($tag) + } +} +#--------------------------------------------------------------------- +# defaultStringType - set or query default type for unrestricted strings +#--------------------------------------------------------------------- +proc ::asn::defaultStringType {{type {}}} { + variable defaultStringType + if {![string length $type]} { + return $defaultStringType + } + if {$type ne "BMP" && $type ne "UTF8"} { + return -code error "Invalid default string type. Should be one of BMP, UTF8" + } + set defaultStringType $type + return +} + +#--------------------------------------------------------------------------- +# asnString - encode readable string into most restricted type possible +#--------------------------------------------------------------------------- + +proc ::asn::asnString {string} { + variable nonPrintableChars + variable nonNumericChars + if {[string length $string]!=[string bytelength $string]} { + # There are non-ascii character + variable defaultStringType + return [asn${defaultStringType}String $string] + } elseif {![regexp $nonNumericChars $string]} { + return [asnNumericString $string] + } elseif {![regexp $nonPrintableChars $string]} { + return [asnPrintableString $string] + } else { + return [asnIA5String $string] + } +} + +#----------------------------------------------------------------------------- +package provide asn 0.8.4 + diff --git a/tcllib/modules/asn/asn.test b/tcllib/modules/asn/asn.test new file mode 100644 index 0000000..9ec628b --- /dev/null +++ b/tcllib/modules/asn/asn.test @@ -0,0 +1,956 @@ +# -*- tcl -*- +# asn.test: tests for the asn BER encoding/decoding module. +# +# Copyright (c) 2004 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Copyright (c) 2004-2007 by Michael Schlenker <mic42@users.sourceforge.net> +# All rights reserved. +# +# RCS: @(#) $Id: asn.test,v 1.19 2011/01/05 22:33:33 mic42 Exp $ + +# ------------------------------------------------------------------------- + + +source [file join \ + [file dirname [file dirname [file join [pwd] [info script]]]] \ + devtools testutilities.tcl] + + +testsNeedTcl 8.4 +testsNeedTcltest 1.0 + +testing { + useLocal asn.tcl asn +} + +# Converts binary encoded structure into hexadecimal dump +# which is more readable in test results +# Allows cut'n'paste both encoded and parsed OID from dumpasn1.cfg +# +proc bytes2hex {string} { + foreach b [split $string ""] { + lappend l [format %02X [scan $b %c]] + } + return [join $l " "] +} + +# ------------------------------------------------------------------------- + +test asn-1.0 {integer} { + catch {asn::asnInteger} result + set result +} [tcltest::wrongNumArgs {asn::asnInteger} {number} 0] + +test asn-1.1 {integer} { + catch {asn::asnInteger a b} result + set result +} [tcltest::tooManyArgs {asn::asnInteger} {number}] + +test asn-1.2 {integer} { + catch {asn::asnInteger a} result + set result +} {expected integer but got "a"} + + +test asn-3.0 {enum} { + catch {asn::asnEnumeration} result + set result +} [tcltest::wrongNumArgs {asn::asnEnumeration} {number} 0] + +test asn-3.1 {enum} { + catch {asn::asnEnumeration a b} result + set result +} [tcltest::tooManyArgs {asn::asnEnumeration} {number}] + +test asn-3.2 {enum} { + catch {asn::asnEnumeration a} result + set result +} {expected integer but got "a"} + + + + +foreach {n i len hex} { + 0 0 01 00 + 1 -1 01 FF + 2 1 01 01 + 3 127 01 7F + 4 128 02 0080 + 5 129 02 0081 + 6 256 02 0100 + 7 -127 01 81 + 8 -128 01 80 + 9 -129 02 FF7F + 10 32766 02 7FFE + 11 32767 02 7FFF + 12 32768 03 008000 + 13 32769 03 008001 + 14 -32767 02 8001 + 15 -32768 02 8000 + 16 -32769 03 FF7FFF + 17 65536 03 010000 + 18 8388607 03 7FFFFF + 19 8388608 04 00800000 + 20 8388609 04 00800001 + 21 16777216 04 01000000 + 22 -8388607 03 800001 + 23 -8388608 03 800000 + 24 -8388609 04 FF7FFFFF + 25 -65536 03 FF0000 + 26 -2147483648 04 80000000 + 27 2147483647 04 7FFFFFFF + 28 -549755813888 05 8000000000 + 29 549755813887 05 7FFFFFFFFF + 30 -140737488355328 06 800000000000 + 31 140737488355327 06 7FFFFFFFFFFF + 32 -36028797018963968 07 80000000000000 + 33 36028797018963967 07 7FFFFFFFFFFFFF + 34 36028797018963968 08 0080000000000000 + 35 -9223372036854775808 08 8000000000000000 + 36 9223372036854775807 08 7FFFFFFFFFFFFFFF +} { + test asn-2.$n {integer} { + binary scan [asn::asnInteger $i] H* result + list $i [string toupper $result] + } [list $i 02$len$hex] ; # {} + + test asn-4.$n {enum} { + binary scan [asn::asnEnumeration $i] H* result + list $i [string toupper $result] + } [list $i 0A$len$hex] ; # {} +} + +test asn-5.0 {boolean} { + catch {asn::asnBoolean} result + set result +} [tcltest::wrongNumArgs {asn::asnBoolean} {bool} 0] + +test asn-5.1 {boolean} { + catch {asn::asnBoolean a b} result + set result +} [tcltest::tooManyArgs {asn::asnBoolean} {bool}] + +test asn-5.2 {boolean} { + catch {asn::asnBoolean a} result + set result +} {expected boolean value but got "a"} + +test asn-5.3 {boolean - true} { + binary scan [asn::asnBoolean 1] H* result + string toupper $result +} {0101FF} + +test asn-5.4 {boolean - false} { + binary scan [asn::asnBoolean 0] H* result + string toupper $result +} {010100} + +test asn-6.0 {parse boolean} { + catch {asn::asnGetBoolean} result + set result +} [tcltest::wrongNumArgs {asn::asnGetBoolean} {data_var bool_var} 0] + +test asn-6.1 {parse boolean} { + catch {asn::asnGetBoolean a} result + set result +} [tcltest::wrongNumArgs {asn::asnGetBoolean} {data_var bool_var} 1] + +test asn-6.2 {parse boolean} { + catch {asn::asnGetBoolean a b c} result + set result +} [tcltest::tooManyArgs {asn::asnGetBoolean} {data_var bool_var}] + +test asn-6.3 {parse boolean} { + catch {asn::asnGetBoolean a b} result + set result +} {can't read "data": no such variable} + +test asn-6.4 {parse boolean - wrong tag} { + set a \x02\x01\x00 + catch {asn::asnGetBoolean a b} result + set result +} {Expected Boolean (0x01), but got 02} + +test asn-6.5 {parse boolean - wrong length} { + set a \x01\x02\x00 + catch {asn::asnGetBoolean a b} result + list $result $b +} [list "" 0] + +test asn-6.6 {parse boolean - true} { + set a \x01\x01\xFF + asn::asnGetBoolean a b + set b +} 1 + +test asn-6.7 {parse boolean - true} { + set a \x01\x01\x01 + asn::asnGetBoolean a b + set b +} 1 + +test asn-6.8 {parse boolean - false} { + set a \x01\x01\x00 + asn::asnGetBoolean a b + set b +} 0 + +test asn-7.0 {null} { + catch {asn::asnNull foo} result + set result +} [tcltest::tooManyArgs {asn::asnNull} {}] + +test asn-7.1 {null} { + binary scan [asn::asnNull] H* result + set result +} {0500} + +test asn-8.0 {parse null} { + catch {asn::asnGetNull} result + set result +} [tcltest::wrongNumArgs asn::asnGetNull {data_var} 0] + +test asn-8.1 {parse null} { + catch {asn::asnGetNull foo bar} result + set result +} [tcltest::tooManyArgs {asn::asnGetNull} {data_var}] + +test asn-8.2 {parse null} { + set wrongtag \x01\x01 + catch {asn::asnGetNull wrongtag} result + set result +} {Expected NULL (0x05), but got 01} + +test asn-8.3 {parse null} { + set wronglength \x05\x01 + catch {asn::asnGetNull wronglength} result + set result +} {} + +test asn-8.4 {parse null} { + set null \x05\x00 + asn::asnGetNull null +} {} + +package require math::bignum +foreach {n i len hex} { + 0 0 01 00 + 1 -1 01 FF + 2 1 01 01 + 3 127 01 7F + 4 128 02 0080 + 5 129 02 0081 + 6 256 02 0100 + 7 -127 01 81 + 8 -128 01 80 + 9 -129 02 FF7F + 10 32766 02 7FFE + 11 32767 02 7FFF + 12 32768 03 008000 + 13 32769 03 008001 + 14 -32767 02 8001 + 15 -32768 02 8000 + 16 -32769 03 FF7FFF + 17 65536 03 010000 + 18 8388607 03 7FFFFF + 19 8388608 04 00800000 + 20 8388609 04 00800001 + 21 16777216 04 01000000 + 22 -8388607 03 800001 + 23 -8388608 03 800000 + 24 -8388609 04 FF7FFFFF + 25 -65536 03 FF0000 +} { + test asn-9.$n {big integer} { + binary scan [asn::asnBigInteger [math::bignum::fromstr $i]] H* result + list $i [string toupper $result] + } [list $i 02$len$hex] ; # {} + +} + +foreach {n len hex} { + 0 0 00 + 1 1 01 + 2 127 7F + 3 128 8180 + 4 129 8181 + 5 255 81FF + 6 256 820100 + 7 32767 827FFF + 8 32768 828000 + 9 32769 828001 + 10 65535 82FFFF + 11 65536 83010000 + 12 8388607 837FFFFF + 13 8388608 83800000 + 14 8388609 83800001 + 15 16777215 83FFFFFF + 16 16777216 8401000000 + 17 4294967295 84FFFFFFFF + 18 4294967296 850100000000 + 19 1099511627775 85FFFFFFFFFF + 20 1099511627776 86010000000000 + 21 281474976710655 86FFFFFFFFFFFF + 22 281474976710656 8701000000000000 + 23 72057594037927935 87FFFFFFFFFFFFFF + 24 72057594037927936 880100000000000000 + 25 9223372036854775807 887FFFFFFFFFFFFFFF + } { + test asn-10.$n {asnLength encoding} { + binary scan [asn::asnLength $len] H* result + string toupper $result + } $hex +} + +foreach {n len hex} { + 0 0 00 + 1 1 01 + 2 127 7F + 3 128 8180 + 4 129 8181 + 5 255 81FF + 6 256 820100 + 7 32767 827FFF + 8 32768 828000 + 9 32769 828001 + 10 65535 82FFFF + 11 65536 83010000 + 12 8388607 837FFFFF + 13 8388608 83800000 + 14 8388609 83800001 + 15 16777215 83FFFFFF + 16 16777216 8401000000 + 17 4294967295 84FFFFFFFF + 18 4294967296 850100000000 + 19 1099511627775 85FFFFFFFFFF + 20 1099511627776 86010000000000 + 21 281474976710655 86FFFFFFFFFFFF + 22 281474976710656 8701000000000000 + 23 72057594037927935 87FFFFFFFFFFFFFF + 24 72057594037927936 880100000000000000 + 25 9223372036854775807 887FFFFFFFFFFFFFFF + } { + test asn-11.$n {asnGetLength decoding} { + set data [binary format H* $hex ] + asn::asnGetLength data length + set length + } $len +} + +foreach {n i len hex} { + 0 0 01 00 + 1 -1 01 FF + 2 1 01 01 + 3 127 01 7F + 4 128 02 0080 + 5 129 02 0081 + 6 256 02 0100 + 7 -127 01 81 + 8 -128 01 80 + 9 -129 02 FF7F + 10 32766 02 7FFE + 11 32767 02 7FFF + 12 32768 03 008000 + 13 32769 03 008001 + 14 -32767 02 8001 + 15 -32768 02 8000 + 16 -32769 03 FF7FFF + 17 65536 03 010000 + 18 8388607 03 7FFFFF + 19 8388608 04 00800000 + 20 8388609 04 00800001 + 21 16777216 04 01000000 + 22 -8388607 03 800001 + 23 -8388608 03 800000 + 24 -8388609 04 FF7FFFFF + 25 -65536 03 FF0000 + 26 -2147483648 04 80000000 + 27 2147483647 04 7FFFFFFF + 28 -549755813888 05 8000000000 + 29 549755813887 05 7FFFFFFFFF + 30 -140737488355328 06 800000000000 + 31 140737488355327 06 7FFFFFFFFFFF + 32 -36028797018963968 07 80000000000000 + 33 36028797018963967 07 7FFFFFFFFFFFFF + 34 36028797018963968 08 0080000000000000 + 35 -9223372036854775808 08 8000000000000000 + 36 9223372036854775807 08 7FFFFFFFFFFFFFFF + 37 65537 03 010001 + 38 -8323071 03 810001 +} { + test asn-12.$n {getInteger} { + set data [binary format H2H2H* 02 $len $hex] + asn::asnGetInteger data int + set int + } $i ; # {} +} + +foreach {n i len hex} { + 0 0 01 00 + 1 -1 01 FF + 2 1 01 01 + 3 127 01 7F + 4 128 02 0080 + 5 129 02 0081 + 6 256 02 0100 + 7 -127 01 81 + 8 -128 01 80 + 9 -129 02 FF7F + 10 32766 02 7FFE + 11 32767 02 7FFF + 12 32768 03 008000 + 13 32769 03 008001 + 14 -32767 02 8001 + 15 -32768 02 8000 + 16 -32769 03 FF7FFF + 17 65536 03 010000 + 18 8388607 03 7FFFFF + 19 8388608 04 00800000 + 20 8388609 04 00800001 + 21 16777216 04 01000000 + 22 -8388607 03 800001 + 23 -8388608 03 800000 + 24 -8388609 04 FF7FFFFF + 25 -65536 03 FF0000 + 26 -2147483648 04 80000000 + 27 2147483647 04 7FFFFFFF + 28 -549755813888 05 8000000000 + 29 549755813887 05 7FFFFFFFFF + 30 -140737488355328 06 800000000000 + 31 140737488355327 06 7FFFFFFFFFFF + 32 -36028797018963968 07 80000000000000 + 33 36028797018963967 07 7FFFFFFFFFFFFF + 34 36028797018963968 08 0080000000000000 + 35 -9223372036854775808 08 8000000000000000 + 36 9223372036854775807 08 7FFFFFFFFFFFFFFF + 37 65537 03 010001 + 38 -8323071 03 810001 +} { + test asn-12.[expr {$n+39}] {getBigInteger} { + set data [binary format H2H2H* 02 $len $hex] + asn::asnGetBigInteger data int + math::bignum::tostr $int + } $i ; # {} +} + +test asn-13.0 {peekByte} { + set data \x0d\x0a + asn::asnPeekByte data byte + list $byte [string length $data] +} {13 2} + +test asn-14.0 {getByte} { + set data \x0d\x0a + asn::asnGetByte data byte + list $byte [string length $data] +} {13 1} + +test asn-15.0 {getBytes} { + set data \x0d\x0d\x0d\x0d\x0a + asn::asnGetBytes data 4 bytes + list [string length $data] [string length $bytes] +} [list 1 4] + +test asn-15.1 {getBytes} { + set data \x0d\x0d\x0d\x0d\x0a + asn::asnGetBytes data 4 bytes + set expectedbytes \x0d\x0d\x0d\x0d + set expecteddata \x0a + list [expr {$data == $expecteddata}] [expr {$bytes == $expectedbytes}] +} [list 1 1] + +# 16 ----------- string encoder/decoder invalid arguments +array set stringtag { + NumericString 0x12 + PrintableString 0x13 + IA5String 0x16 + BMPString 0x1e + UTF8String 0x0c +} +set i 0 +foreach strtype {NumericString PrintableString IA5String BMPString UTF8String} { + incr i + test asn-16.$i $strtype { + catch {asn::asn$strtype} result + set result + } [tcltest::wrongNumArgs asn::asn$strtype string 0] + incr i + test asn-16.$i $strtype { + catch "asn::asn$strtype a b" result + set result + } [tcltest::tooManyArgs "asn::asn$strtype" string] + incr i + test asn-16.$i get$strtype { + catch "asn::asnGet$strtype foo" result + set result + } [tcltest::wrongNumArgs "asn::asnGet$strtype" "data_var print_var" 0] + incr i + test asn-16.$i get$strtype { + catch "asn::asnGet$strtype foo bar baz" result + set result + } [tcltest::tooManyArgs "asn::asnGet$strtype" "data_var print_var"] + incr i + test asn-16.$i "get$strtype parse sequence" { + set data "\x30\x03abc" + catch "asn::asnGet$strtype data print" result + set result + } "Expected [regsub String $strtype " String"] ($stringtag($strtype)), but got 30" +} +incr i +# 17 ------------------- invalid string values + + +test asn-17.1 {numeric string with non-numbers} { + catch {asn::asnNumericString this-is-not-a-number} result + set result +} "Illegal character in Numeric String." + +test asn-17.2 {numeric string with hexadecimals} { + catch {asn::asnNumericString 09AB} result + set result +} "Illegal character in Numeric String." + +test asn-17.3 {numeric string with spaces - spaces are legal} { + catch {asn::asnNumericString " 093"} +} 0 ;# TCL_OK + +test asn-17.4 {numeric string with minus sign} { + catch {asn::asnNumericString "-15"} result + set result +} "Illegal character in Numeric String." + +test asn-17.5 {numeric string with tab (illegal)} { + catch {asn::asnNumericString "\t093"} result + set result +} "Illegal character in Numeric String." + +#According to ITU-T X.680 37.4 +set printablechars {[-A-Za-z0-9 '()+,./=?:]} + +set i 6 + +for {set j 1} {$j<128} {incr j;incr i} { + set data [format %c $j] + if {[regexp "^$printablechars+\$" $data]} { + test asn-17.$i "printable string valid char [format %02x $j]" { + catch {asn::asnPrintableString $data} + } 0 ; # {} + } else { + test asn-17.$i "printable string invalid char [format %02x $j]" { + catch {asn::asnPrintableString $data} result + set result + } "Illegal character in PrintableString." ; # {} + } +} + +test asn-17.134 {IA5String with Latin-1 char} { + catch {asn::asnIA5String "\xD0"} result + set result +} "Illegal character in IA5String" + +test asn-17.135 {IA5String with Cyrillic chars} { + catch {asn::asnIA5String "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"} result + set result +} "Illegal character in IA5String" + +# 18 - correct encoding of string values + +test asn-18.1 {encode numeric string} { + catch {asn::asnNumericString 123} result + set result +} "\x12\003123" + +test asn-18.2 {encode numeric strin with space} { + catch {asn::asnNumericString "1 2 3"} result + set result +} "\x12\0051 2 3" + +test asn-18.3 {encode printable string} { + catch {asn::asnPrintableString "printable string"} result + set result +} "\x13\x10printable string" + +test asn-18.4 {encode IA5 string} { + catch {asn::asnIA5String "vitus@45.free.net"} result + set result +} "\x16\x11vitus@45.free.net" + +test asn-18.5 {encode bmp string US-ASCII} { + catch {asn::asnBMPString "US-ASCII"} result + set result +} "\x1e\x10\0U\0S\0-\0A\0S\0C\0I\0I" + +test asn-18.6 {encode UTF8 string US-ASCII} { + catch {asn::asnUTF8String "US-ASCII"} result + set result +} "\x0c\x08US-ASCII" + +test asn-18.7 {encode bmp string latin-1} { + catch {asn::asnBMPString "gar\xC7on"} result + set result +} "\x1e\x0c\0g\0a\0r\0\xc7\0o\0n" + +test asn-18.8 {encode utf-8 string latin-1} { + catch {asn::asnUTF8String "gar\xC7on"} result + set result +} "\x0c\x07gar\xc3\x87on" + +test asn-18.9 {encode bmp string cyrillic} { + catch {asn::asnBMPString "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"} result + set result +} "\x1e\x0e\x04\x40\x04\x43\x04\x41\x04\x41\x04\x3a\x04\x38\x04\x39" + +test asn-18.10 {encode UTF8 string cyrillic} { + catch {asn::asnUTF8String "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"} result + set result +} "\x0c\x0e\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9" + +test asn-18.11 {decode numeric string} { + set data "\x12\003123" + asn::asnGetNumericString data print + set print +} 123 + +test asn-18.12 {decode printable string} { + set data "\x13\x10printable string" + asn::asnGetPrintableString data print + set print +} "printable string" + +test asn-18.13 {decode IA5 string} { + set data "\x16\x11vitus@45.free.net" + asn::asnGetIA5String data print + set print +} "vitus@45.free.net" + +test asn-18.14 {decode BMP string US-ASCII} { + set data "\x1e\x10\0U\0S\0-\0A\0S\0C\0I\0I" + asn::asnGetBMPString data print + set print +} "US-ASCII" + +test asn-18.15 {decode UTF8 string US-ASCII} { + set data "\x0c\x08US-ASCII" + asn::asnGetUTF8String data print + set print +} "US-ASCII" + +test asn-18.16 {decode BMP string latin-1} { + set data "\x1e\x0c\0g\0a\0r\0\xc7\0o\0n" + asn::asnGetBMPString data print + set print +} "gar\xC7on" + +test asn-18.17 {decode UTF8 string latin-1} { + set data "\x0c\x07gar\xc3\x87on" + asn::asnGetUTF8String data print + set print +} "gar\xC7on" + +test asn-18.18 {decode BMP string cyrillic} { + set data "\x1e\x0e\x04\x40\x04\x43\x04\x41\x04\x41\x04\x3a\x04\x38\x04\x39" + asn::asnGetBMPString data print + set print +} "\u0440\u0443\u0441\u0441\u043a\u0438\u0439" + +test asn-18.19 {decode UTF8 string cyrillic} { + set data "\x0c\x0e\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9" + asn::asnGetUTF8String data print + set print +} "\u0440\u0443\u0441\u0441\u043a\u0438\u0439" + +# 19 ------- multitype getString +set i 0 +foreach {type encoded str} { + +NumericString "\x12\003123" 123 +PrintableString "\x13\x10printable string" "printable string" +IA5String "\x16\x11vitus@45.free.net" "vitus@45.free.net" +BMPString "\x1e\x10\0U\0S\0-\0A\0S\0C\0I\0I" US-ASCII +UTF8String "\x0c\x08US-ASCII" US-ASCII +BMPString "\x1e\x0c\0g\0a\0r\0\xc7\0o\0n" "gar\xc7on" +UTF8String "\x0c\x07gar\xc3\x87on" "gar\xc7on" +BMPString "\x1e\x0e\x04\x40\x04\x43\x04\x41\x04\x41\x04\x3a\x04\x38\x04\x39" +"\u0440\u0443\u0441\u0441\u043a\u0438\u0439" +UTF8String "\x0c\x0e\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9" +"\u0440\u0443\u0441\u0441\u043a\u0438\u0439"} { +incr i + test asn-19.$i "getString - decode $type" { + set data $encoded + asn::asnGetString data print + set print + } $str + incr i + test asn-19.$i "getString - decode $type and get its type" { + set data $encoded + asn::asnGetString data print gotType + list $data $print $gotType + } [list {} $str $type] +} + +# 20 ----- multitype String encoding + +test asn-20.1 {Set default type to something wrong} { + catch {asn::defaultStringType foo} result + set result +} "Invalid default string type. Should be one of BMP, UTF8" + +test asn-20.2 {Set default value to string type which cannot hold any char} { + catch {asn::defaultStringType IA5} result + set result +} "Invalid default string type. Should be one of BMP, UTF8" + + +test asn-20.3 {Set default type to UTF8} { + asn::defaultStringType UTF8 +} "" + +test asn-20.4 {Get default string type} { + asn::defaultStringType +} UTF8 + +test asn-20.5 {String - encode numeric value} { + asn::asnString 123 +} "\x12\003123" + +test asn-20.6 {String - encode printable value} { + asn::asnString "printable string" +} "\x13\x10printable string" + +test asn-20.7 {String - encode ASCII value} { + asn::asnString vitus@45.free.net +} "\x16\x11vitus@45.free.net" + +test asn-20.8 {String - encode Latin-1 value} { + asn::asnString "gar\xc7on" +} "\x0c\x07gar\xc3\x87on" + +test asn-20.9 {String - encode Cyrillic value} { + asn::asnString "\u0440\u0443\u0441\u0441\u043a\u0438\u0439" +} "\x0c\x0e\xd1\x80\xd1\x83\xd1\x81\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9" + +test asn-20.10 {Set default string type to BMP} { + asn::defaultStringType BMP + asn::defaultStringType +} BMP + +test asn-20.11 {String - encode numeric value} { + asn::asnString 123 +} "\x12\003123" + +test asn-20.12 {String - encode printable value} { + asn::asnString "printable string" +} "\x13\x10printable string" + +test asn-20.13 {String - encode ASCII value} { + asn::asnString vitus@45.free.net +} "\x16\x11vitus@45.free.net" + + +test asn-20.14 {String - encode Latin-1 value} { + asn::asnString "gar\xc7on" +} "\x1e\x0c\0g\0a\0r\0\xc7\0o\0n" + +test asn-20.15 {String - encode Cyrillic value} { + asn::asnString "\u0440\u0443\u0441\u0441\u043a\u0438\u0439" +} "\x1e\x0e\x04\x40\x04\x43\x04\x41\x04\x41\x04\x3a\x04\x38\x04\x39" + +# 21 --------- Object identifier +# + +test asn-21.1 {ObjectIdentifier start with 0} { + bytes2hex [asn::asnObjectIdentifier {0 2 262 1 10}] +} "06 05 02 82 06 01 0A" + + +test asn-21.2 {ObjectIdentifier start with 1} { + bytes2hex [asn::asnObjectIdentifier {1 2 840 10045 2 1}] +} "06 07 2A 86 48 CE 3D 02 01" + +test asn-21.3 {ObjectIdentifer field > 65536} { + bytes2hex [asn::asnObjectIdentifier {1 2 840 113533 7 66 3}] +} "06 09 2A 86 48 86 F6 7D 07 42 03" + +test asn-21.4 {ObjectIdentifer 2.23.42.9.37} { + bytes2hex [asn::asnObjectIdentifier {2 23 42 9 37}] +} "06 04 67 2A 09 25" + +test asn-21.5 {GetObjectIdentifier 0.2.262.1.10} { + set data "\x06\x05\x02\x82\x06\x01\x0A" + asn::asnGetObjectIdentifier data print + set print +} {0 2 262 1 10} + +test asn-21.6 {GetObjectIdentifier 1 2 840 10045 2 1} { + set data "\x06\x07\x2A\x86\x48\xCE\x3D\x02\x01" + asn::asnGetObjectIdentifier data print + set print +} {1 2 840 10045 2 1} + +test asn-21.7 {GetObjectIdentifier 1 2 840 113533 7 66 3} { + set data "\x06\x09\x2A\x86\x48\x86\xF6\x7D\x07\x42\x03" + asn::asnGetObjectIdentifier data print + set print +} {1 2 840 113533 7 66 3} + +# 22 --- Octet String + +# smoke tests to check that we can at least call the commands +test asn-23.0 {asnContext smoke test} { + set data "\x00" + bytes2hex [asn::asnContext 1 $data] +} {81 01 00} + +test asn-24.0 {asnSequence smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnSequence 1 $data] +} {30 03 31 05 00} + +test asn-25.0 {asnSet smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnSet 1 $data] +} {31 03 31 05 00} + +test asn-26.0 {asnApplicationConstr smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnApplicationConstr 1 $data] +} {61 02 05 00} + +test asn-27.0 {asnApplication smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnApplication 1 $data] +} {41 02 05 00} + +test asn-28.0 {asnContextConstr smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnContextConstr 1 $data] +} {A1 02 05 00} + +test asn-29.0 {asnChoice smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnChoice 1 $data] +} {81 02 05 00} + +test asn-30.0 {asnChoiceConstr smoke test} { + set data [asn::asnNull] + bytes2hex [asn::asnChoiceConstr 1 $data] +} {A1 02 05 00} + +test asn-31.0 {asnPeekTag smoke test} { + set data [asn::asnNull] + asn::asnPeekTag data tag tagtype constr + list $tag $tagtype $constr +} {5 UNIVERSAL 0} + +foreach {n hex tag type constr} { + 1 05 5 UNIVERSAL 0 + 2 1E 30 UNIVERSAL 0 + 3 1F1F 31 UNIVERSAL 0 + 4 5F1F 31 APPLICATION 0 + 5 1F818000 16384 UNIVERSAL 0 + 6 45 5 APPLICATION 0 + 7 65 5 APPLICATION 1 + 8 85 5 CONTEXT 0 + 9 a5 5 CONTEXT 1 + 10 c5 5 PRIVATE 0 + 11 e5 5 PRIVATE 1 + 12 25 5 UNIVERSAL 1 +} { + test asn-31.$n "asnPeekTag $tag $type $constr" \ + "set data \[binary format H* $hex\] + asn::asnPeekTag data tag tagtype constr + list \$tag \$tagtype \$constr" \ + [list $tag $type $constr] +} + +test asn-32.0 {asnTag smoke test} { + bytes2hex [asn::asnTag 5 UNIVERSAL P] +} {05} + +test asn-32.1 {asnTag short tag} { + bytes2hex [asn::asnTag 30 UNIVERSAL P] +} {1E} + +test asn-32.2 {asnTag long tag} { + bytes2hex [asn::asnTag 31 UNIVERSAL P] +} {1F 1F} + +test asn-32.3 {asnTag long tag} { + bytes2hex [asn::asnTag 31 APPLICATION P] +} {5F 1F} + +test asn-32.4 {asnTag long tag} { + bytes2hex [asn::asnTag 127 UNIVERSAL P] +} {1F 7F} + +test asn-32.5 {asnTag long tag} { + bytes2hex [asn::asnTag 128 UNIVERSAL P] +} {1F 81 00} + +test asn-32.6 {asnTag long tag} { + bytes2hex [asn::asnTag 16384 UNIVERSAL P] +} {1F 81 80 00} + +test asn-32.7 {asnTag long tag} { + bytes2hex [asn::asnTag 16385 UNIVERSAL P] +} {1F 81 80 01} + +test asn-32.8 {asnTag tag APPLICATION, PRIMITIVE} { + bytes2hex [asn::asnTag 5 APPLICATION P] +} {45} + +test asn-32.9 {asnTag tag APPLICATION, CONSTRUCTED} { + bytes2hex [asn::asnTag 5 APPLICATION C] +} {65} + +test asn-32.10 {asnTag tag CONTEXT, PRIMITIVE} { + bytes2hex [asn::asnTag 5 CONTEXT P] +} {85} + +test asn-32.11 {asnTag tag CONTEXT, CONSTRUCTED} { + bytes2hex [asn::asnTag 5 CONTEXT C] +} {A5} + +test asn-32.12 {asnTag tag PRIVATE,PRIMITIVE} { + bytes2hex [asn::asnTag 5 PRIVATE P] +} {C5} + +test asn-32.13 {asnTag tag PRIVATE,CONSTRUCTED} { + bytes2hex [asn::asnTag 5 PRIVATE C] +} {E5} + +test asn-32.14 {asnTag tag UNIVERSAL, CONSTRUCTED} { + bytes2hex [asn::asnTag 5 UNIVERSAL C] +} {25} + +foreach {n hex bin} { + 1 03020780 1 + 2 030206c0 11 + 3 03020680 10 + 4 030200ff 11111111 + 5 03020000 00000000 + 6 0303078000 100000000 +} { + test asn-33.$n "asnBitstring $bin" \ + "binary scan \[asn::asnBitString $bin\] H* val + set val" \ + $hex +} + +foreach {n hex bin} { + 1 03020780 1 + 2 030206c0 11 + 3 03020680 10 + 4 030200ff 11111111 + 5 03020000 00000000 + 6 0303078000 100000000 +} { + test asn-34.$n "asnGetBitstring $bin" \ + "set data \[binary format H* $hex\] + asn::asnGetBitString data bits + set bits " \ + $bin +} + + +testsuiteCleanup + diff --git a/tcllib/modules/asn/laymans_guide.txt b/tcllib/modules/asn/laymans_guide.txt new file mode 100644 index 0000000..d4fbe64 --- /dev/null +++ b/tcllib/modules/asn/laymans_guide.txt @@ -0,0 +1,1855 @@ +A Layman's Guide to a Subset of ASN.1, BER, and DER + +An RSA Laboratories Technical Note +Burton S. Kaliski Jr. +Revised November 1, 1993 + + +Supersedes June 3, 1991 version, which was also published as +NIST/OSI Implementors' Workshop document SEC-SIG-91-17. +PKCS documents are available by electronic mail to +<pkcs@rsa.com>. + +Copyright (C) 1991-1993 RSA Laboratories, a division of RSA +Data Security, Inc. License to copy this document is granted +provided that it is identified as "RSA Data Security, Inc. +Public-Key Cryptography Standards (PKCS)" in all material +mentioning or referencing this document. +003-903015-110-000-000 + + +Abstract. This note gives a layman's introduction to a +subset of OSI's Abstract Syntax Notation One (ASN.1), Basic +Encoding Rules (BER), and Distinguished Encoding Rules +(DER). The particular purpose of this note is to provide +background material sufficient for understanding and +implementing the PKCS family of standards. + + +1. Introduction + +It is a generally accepted design principle that abstraction +is a key to managing software development. With abstraction, +a designer can specify a part of a system without concern +for how the part is actually implemented or represented. +Such a practice leaves the implementation open; it +simplifies the specification; and it makes it possible to +state "axioms" about the part that can be proved when the +part is implemented, and assumed when the part is employed +in another, higher-level part. Abstraction is the hallmark +of most modern software specifications. + +One of the most complex systems today, and one that also +involves a great deal of abstraction, is Open Systems +Interconnection (OSI, described in X.200). OSI is an +internationally standardized architecture that governs the +interconnection of computers from the physical layer up to +the user application layer. Objects at higher layers are +defined abstractly and intended to be implemented with +objects at lower layers. For instance, a service at one +layer may require transfer of certain abstract objects +between computers; a lower layer may provide transfer +services for strings of ones and zeroes, using encoding +rules to transform the abstract objects into such strings. +OSI is called an open system because it supports many +different implementations of the services at each layer. + +OSI's method of specifying abstract objects is called ASN.1 +(Abstract Syntax Notation One, defined in X.208), and one +set of rules for representing such objects as strings of +ones and zeros is called the BER (Basic Encoding Rules, +defined in X.209). ASN.1 is a flexible notation that allows +one to define a variety data types, from simple types such +as integers and bit strings to structured types such as sets +and sequences, as well as complex types defined in terms of +others. BER describes how to represent or encode values of +each ASN.1 type as a string of eight-bit octets. There is +generally more than one way to BER-encode a given value. +Another set of rules, called the Distinguished Encoding +Rules (DER), which is a subset of BER, gives a unique +encoding to each ASN.1 value. + +The purpose of this note is to describe a subset of ASN.1, +BER and DER sufficient to understand and implement one OSI- +based application, RSA Data Security, Inc.'s Public-Key +Cryptography Standards. The features described include an +overview of ASN.1, BER, and DER and an abridged list of +ASN.1 types and their BER and DER encodings. Sections 2-4 +give an overview of ASN.1, BER, and DER, in that order. +Section 5 lists some ASN.1 types, giving their notation, +specific encoding rules, examples, and comments about their +application to PKCS. Section 6 concludes with an example, +X.500 distinguished names. + +Advanced features of ASN.1, such as macros, are not +described in this note, as they are not needed to implement +PKCS. For information on the other features, and for more +detail generally, the reader is referred to CCITT +Recommendations X.208 and X.209, which define ASN.1 and BER. + +Terminology and notation. In this note, an octet is an eight- +bit unsigned integer. Bit 8 of the octet is the most +significant and bit 1 is the least significant. + +The following meta-syntax is used for in describing ASN.1 +notation: + + BIT monospace denotes literal characters in the type + and value notation; in examples, it generally + denotes an octet value in hexadecimal + + n1 bold italics denotes a variable + + [] bold square brackets indicate that a term is + optional + + {} bold braces group related terms + + | bold vertical bar delimits alternatives with a + group + + ... bold ellipsis indicates repeated occurrences + + = bold equals sign expresses terms as subterms + + +2. Abstract Syntax Notation One + +Abstract Syntax Notation One, abbreviated ASN.1, is a +notation for describing abstract types and values. + +In ASN.1, a type is a set of values. For some types, there +are a finite number of values, and for other types there are +an infinite number. A value of a given ASN.1 type is an +element of the type's set. ASN.1 has four kinds of type: +simple types, which are "atomic" and have no components; +structured types, which have components; tagged types, which +are derived from other types; and other types, which include +the CHOICE type and the ANY type. Types and values can be +given names with the ASN.1 assignment operator (::=) , and +those names can be used in defining other types and values. + +Every ASN.1 type other than CHOICE and ANY has a tag, which +consists of a class and a nonnegative tag number. ASN.1 +types are abstractly the same if and only if their tag +numbers are the same. In other words, the name of an ASN.1 +type does not affect its abstract meaning, only the tag +does. There are four classes of tag: + + Universal, for types whose meaning is the same in all + applications; these types are only defined in + X.208. + + Application, for types whose meaning is specific to an + application, such as X.500 directory services; + types in two different applications may have the + same application-specific tag and different + meanings. + + Private, for types whose meaning is specific to a given + enterprise. + + Context-specific, for types whose meaning is specific + to a given structured type; context-specific tags + are used to distinguish between component types + with the same underlying tag within the context of + a given structured type, and component types in + two different structured types may have the same + tag and different meanings. + +The types with universal tags are defined in X.208, which +also gives the types' universal tag numbers. Types with +other tags are defined in many places, and are always +obtained by implicit or explicit tagging (see Section 2.3). +Table 1 lists some ASN.1 types and their universal-class +tags. + + Type Tag number Tag number + (decimal) (hexadecimal) + INTEGER 2 02 + BIT STRING 3 03 + OCTET STRING 4 04 + NULL 5 05 + OBJECT IDENTIFIER 6 06 + SEQUENCE and SEQUENCE OF 16 10 + SET and SET OF 17 11 + PrintableString 19 13 + T61String 20 14 + IA5String 22 16 + UTCTime 23 17 + + Table 1. Some types and their universal-class tags. + +ASN.1 types and values are expressed in a flexible, +programming-language-like notation, with the following +special rules: + + o Layout is not significant; multiple spaces and + line breaks can be considered as a single space. + + o Comments are delimited by pairs of hyphens (--), + or a pair of hyphens and a line break. + + o Identifiers (names of values and fields) and type + references (names of types) consist of upper- and + lower-case letters, digits, hyphens, and spaces; + identifiers begin with lower-case letters; type + references begin with upper-case letters. + +The following four subsections give an overview of simple +types, structured types, implicitly and explicitly tagged +types, and other types. Section 5 describes specific types +in more detail. + + +2.1 Simple types + +Simple types are those not consisting of components; they +are the "atomic" types. ASN.1 defines several; the types +that are relevant to the PKCS standards are the following: + + BIT STRING, an arbitrary string of bits (ones and + zeroes). + + IA5String, an arbitrary string of IA5 (ASCII) + characters. + + INTEGER, an arbitrary integer. + + NULL, a null value. + + OBJECT IDENTIFIER, an object identifier, which is a + sequence of integer components that identify an + object such as an algorithm or attribute type. + + OCTET STRING, an arbitrary string of octets (eight-bit + values). + + PrintableString, an arbitrary string of printable + characters. + + T61String, an arbitrary string of T.61 (eight-bit) + characters. + + UTCTime, a "coordinated universal time" or Greenwich + Mean Time (GMT) value. + +Simple types fall into two categories: string types and non- +string types. BIT STRING, IA5String, OCTET STRING, +PrintableString, T61String, and UTCTime are string types. + +String types can be viewed, for the purposes of encoding, as +consisting of components, where the components are +substrings. This view allows one to encode a value whose +length is not known in advance (e.g., an octet string value +input from a file stream) with a constructed, indefinite- +length encoding (see Section 3). + +The string types can be given size constraints limiting the +length of values. + + +2.2 Structured types + +Structured types are those consisting of components. ASN.1 +defines four, all of which are relevant to the PKCS +standards: + + SEQUENCE, an ordered collection of one or more types. + + SEQUENCE OF, an ordered collection of zero or more + occurrences of a given type. + + SET, an unordered collection of one or more types. + + SET OF, an unordered collection of zero or more + occurrences of a given type. + +The structured types can have optional components, possibly +with default values. + + +2.3 Implicitly and explicitly tagged types + +Tagging is useful to distinguish types within an +application; it is also commonly used to distinguish +component types within a structured type. For instance, +optional components of a SET or SEQUENCE type are typically +given distinct context-specific tags to avoid ambiguity. + +There are two ways to tag a type: implicitly and explicitly. + +Implicitly tagged types are derived from other types by +changing the tag of the underlying type. Implicit tagging is +denoted by the ASN.1 keywords [class number] IMPLICIT (see +Section 5.1). + +Explicitly tagged types are derived from other types by +adding an outer tag to the underlying type. In effect, +explicitly tagged types are structured types consisting of +one component, the underlying type. Explicit tagging is +denoted by the ASN.1 keywords [class number] EXPLICIT (see +Section 5.2). + +The keyword [class number] alone is the same as explicit +tagging, except when the "module" in which the ASN.1 type is +defined has implicit tagging by default. ("Modules" are +among the advanced features not described in this note.) + +For purposes of encoding, an implicitly tagged type is +considered the same as the underlying type, except that the +tag is different. An explicitly tagged type is considered +like a structured type with one component, the underlying +type. Implicit tags result in shorter encodings, but +explicit tags may be necessary to avoid ambiguity if the tag +of the underlying type is indeterminate (e.g., the +underlying type is CHOICE or ANY). + + +2.4 Other types + +Other types in ASN.1 include the CHOICE and ANY types. The +CHOICE type denotes a union of one or more alternatives; the +ANY type denotes an arbitrary value of an arbitrary type, +where the arbitrary type is possibly defined in the +registration of an object identifier or integer value. + + +3. Basic Encoding Rules + +The Basic Encoding Rules for ASN.1, abbreviated BER, give +one or more ways to represent any ASN.1 value as an octet +string. (There are certainly other ways to represent ASN.1 +values, but BER is the standard for interchanging such +values in OSI.) + +There are three methods to encode an ASN.1 value under BER, +the choice of which depends on the type of value and whether +the length of the value is known. The three methods are +primitive, definite-length encoding; constructed, definite- +length encoding; and constructed, indefinite-length +encoding. Simple non-string types employ the primitive, +definite-length method; structured types employ either of +the constructed methods; and simple string types employ any +of the methods, depending on whether the length of the value +is known. Types derived by implicit tagging employ the +method of the underlying type and types derived by explicit +tagging employ the constructed methods. + +In each method, the BER encoding has three or four parts: + + Identifier octets. These identify the class and tag + number of the ASN.1 value, and indicate whether + the method is primitive or constructed. + + Length octets. For the definite-length methods, these + give the number of contents octets. For the + constructed, indefinite-length method, these + indicate that the length is indefinite. + + Contents octets. For the primitive, definite-length + method, these give a concrete representation of + the value. For the constructed methods, these + give the concatenation of the BER encodings of the + components of the value. + + End-of-contents octets. For the constructed, indefinite- + length method, these denote the end of the + contents. For the other methods, these are absent. + +The three methods of encoding are described in the following +sections. + + +3.1 Primitive, definite-length method + +This method applies to simple types and types derived from +simple types by implicit tagging. It requires that the +length of the value be known in advance. The parts of the +BER encoding are as follows: + +Identifier octets. There are two forms: low tag number (for +tag numbers between 0 and 30) and high tag number (for tag +numbers 31 and greater). + + Low-tag-number form. One octet. Bits 8 and 7 specify + the class (see Table 2), bit 6 has value "0," + indicating that the encoding is primitive, and + bits 5-1 give the tag number. + + Class Bit Bit + 8 7 + universal 0 0 + application 0 1 + context-specific 1 0 + private 1 1 + + Table 2. Class encoding in identifier octets. + + High-tag-number form. Two or more octets. First octet + is as in low-tag-number form, except that bits 5-1 + all have value "1." Second and following octets + give the tag number, base 128, most significant + digit first, with as few digits as possible, and + with the bit 8 of each octet except the last set + to "1." + +Length octets. There are two forms: short (for lengths +between 0 and 127), and long definite (for lengths between 0 +and 21008-1). + + Short form. One octet. Bit 8 has value "0" and bits 7-1 + give the length. + + Long form. Two to 127 octets. Bit 8 of first octet has + value "1" and bits 7-1 give the number of + additional length octets. Second and following + octets give the length, base 256, most significant + digit first. + +Contents octets. These give a concrete representation of the +value (or the value of the underlying type, if the type is +derived by implicit tagging). Details for particular types +are given in Section 5. + + +3.2 Constructed, definite-length method + +This method applies to simple string types, structured +types, types derived simple string types and structured +types by implicit tagging, and types derived from anything +by explicit tagging. It requires that the length of the +value be known in advance. The parts of the BER encoding are +as follows: + +Identifier octets. As described in Section 3.1, except that +bit 6 has value "1," indicating that the encoding is +constructed. + +Length octets. As described in Section 3.1. + +Contents octets. The concatenation of the BER encodings of +the components of the value: + + o For simple string types and types derived from + them by implicit tagging, the concatenation of the + BER encodings of consecutive substrings of the + value (underlying value for implicit tagging). + + o For structured types and types derived from them + by implicit tagging, the concatenation of the BER + encodings of components of the value (underlying + value for implicit tagging). + + o For types derived from anything by explicit + tagging, the BER encoding of the underlying value. + +Details for particular types are given in Section 5. + + +3.3 Constructed, indefinite-length method + +This method applies to simple string types, structured +types, types derived simple string types and structured +types by implicit tagging, and types derived from anything +by explicit tagging. It does not require that the length of +the value be known in advance. The parts of the BER encoding +are as follows: + +Identifier octets. As described in Section 3.2. + +Length octets. One octet, 80. + +Contents octets. As described in Section 3.2. + +End-of-contents octets. Two octets, 00 00. + +Since the end-of-contents octets appear where an ordinary +BER encoding might be expected (e.g., in the contents octets +of a sequence value), the 00 and 00 appear as identifier and +length octets, respectively. Thus the end-of-contents octets +is really the primitive, definite-length encoding of a value +with universal class, tag number 0, and length 0. + + +4. Distinguished Encoding Rules + +The Distinguished Encoding Rules for ASN.1, abbreviated DER, +are a subset of BER, and give exactly one way to represent +any ASN.1 value as an octet string. DER is intended for +applications in which a unique octet string encoding is +needed, as is the case when a digital signature is computed +on an ASN.1 value. DER is defined in Section 8.7 of X.509. + +DER adds the following restrictions to the rules given in +Section 3: + + 1. When the length is between 0 and 127, the short + form of length must be used + + 2. When the length is 128 or greater, the long form + of length must be used, and the length must be + encoded in the minimum number of octets. + + 3. For simple string types and implicitly tagged + types derived from simple string types, the + primitive, definite-length method must be + employed. + + 4. For structured types, implicitly tagged types + derived from structured types, and explicitly + tagged types derived from anything, the + constructed, definite-length method must be + employed. + +Other restrictions are defined for particular types (such as +BIT STRING, SEQUENCE, SET, and SET OF), and can be found in +Section 5. + + +5. Notation and encodings for some types + +This section gives the notation for some ASN.1 types and +describes how to encode values of those types under both BER +and DER. + +The types described are those presented in Section 2. They +are listed alphabetically here. + +Each description includes ASN.1 notation, BER encoding, and +DER encoding. The focus of the encodings is primarily on the +contents octets; the tag and length octets follow Sections 3 +and 4. The descriptions also explain where each type is used +in PKCS and related standards. ASN.1 notation is generally +only for types, although for the type OBJECT IDENTIFIER, +value notation is given as well. + + +5.1 Implicitly tagged types + +An implicitly tagged type is a type derived from another +type by changing the tag of the underlying type. + +Implicit tagging is used for optional SEQUENCE components +with underlying type other than ANY throughout PKCS, and for +the extendedCertificate alternative of PKCS #7's +ExtendedCertificateOrCertificate type. + +ASN.1 notation: + +[[class] number] IMPLICIT Type + +class = UNIVERSAL | APPLICATION | PRIVATE + +where Type is a type, class is an optional class name, and +number is the tag number within the class, a nonnegative +integer. + +In ASN.1 "modules" whose default tagging method is implicit +tagging, the notation [[class] number] Type is also +acceptable, and the keyword IMPLICIT is implied. (See +Section 2.3.) For definitions stated outside a module, the +explicit inclusion of the keyword IMPLICIT is preferable to +prevent ambiguity. + +If the class name is absent, then the tag is context- +specific. Context-specific tags can only appear in a +component of a structured or CHOICE type. + +Example: PKCS #8's PrivateKeyInfo type has an optional +attributes component with an implicit, context-specific tag: + +PrivateKeyInfo ::= SEQUENCE { + version Version, + privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + privateKey PrivateKey, + attributes [0] IMPLICIT Attributes OPTIONAL } + +Here the underlying type is Attributes, the class is absent +(i.e., context-specific), and the tag number within the +class is 0. + +BER encoding. Primitive or constructed, depending on the +underlying type. Contents octets are as for the BER encoding +of the underlying value. + +Example: The BER encoding of the attributes component of a +PrivateKeyInfo value is as follows: + + o the identifier octets are 80 if the underlying + Attributes value has a primitive BER encoding and + a0 if the underlying Attributes value has a + constructed BER encoding + + o the length and contents octets are the same as the + length and contents octets of the BER encoding of + the underlying Attributes value + +DER encoding. Primitive or constructed, depending on the +underlying type. Contents octets are as for the DER encoding +of the underlying value. + + +5.2 Explicitly tagged types + +Explicit tagging denotes a type derived from another type by +adding an outer tag to the underlying type. + +Explicit tagging is used for optional SEQUENCE components +with underlying type ANY throughout PKCS, and for the +version component of X.509's Certificate type. + +ASN.1 notation: + +[[class] number] EXPLICIT Type + +class = UNIVERSAL | APPLICATION | PRIVATE + +where Type is a type, class is an optional class name, and +number is the tag number within the class, a nonnegative +integer. + +If the class name is absent, then the tag is context- +specific. Context-specific tags can only appear in a +component of a SEQUENCE, SET or CHOICE type. + +In ASN.1 "modules" whose default tagging method is explicit +tagging, the notation [[class] number] Type is also +acceptable, and the keyword EXPLICIT is implied. (See +Section 2.3.) For definitions stated outside a module, the +explicit inclusion of the keyword EXPLICIT is preferable to +prevent ambiguity. + +Example 1: PKCS #7's ContentInfo type has an optional +content component with an explicit, context-specific tag: + +ContentInfo ::= SEQUENCE { + contentType ContentType, + content + [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + +Here the underlying type is ANY DEFINED BY contentType, the +class is absent (i.e., context-specific), and the tag number +within the class is 0. + +Example 2: X.509's Certificate type has a version component +with an explicit, context-specific tag, where the EXPLICIT +keyword is omitted: + +Certificate ::= ... + version [0] Version DEFAULT v1988, +... + +The tag is explicit because the default tagging method for +the ASN.1 "module" in X.509 that defines the Certificate +type is explicit tagging. + +BER encoding. Constructed. Contents octets are the BER +encoding of the underlying value. + +Example: the BER encoding of the content component of a +ContentInfo value is as follows: + + o identifier octets are a0 + + o length octets represent the length of the BER + encoding of the underlying ANY DEFINED BY + contentType value + + o contents octets are the BER encoding of the + underlying ANY DEFINED BY contentType value + +DER encoding. Constructed. Contents octets are the DER +encoding of the underlying value. + + +5.3 ANY + +The ANY type denotes an arbitrary value of an arbitrary +type, where the arbitrary type is possibly defined in the +registration of an object identifier or associated with an +integer index. + +The ANY type is used for content of a particular content +type in PKCS #7's ContentInfo type, for parameters of a +particular algorithm in X.509's AlgorithmIdentifier type, +and for attribute values in X.501's Attribute and +AttributeValueAssertion types. The Attribute type is used by +PKCS #6, #7, #8, #9 and #10, and the AttributeValueAssertion +type is used in X.501 distinguished names. + +ASN.1 notation: + +ANY [DEFINED BY identifier] + +where identifier is an optional identifier. + +In the ANY form, the actual type is indeterminate. + +The ANY DEFINED BY identifier form can only appear in a +component of a SEQUENCE or SET type for which identifier +identifies some other component, and that other component +has type INTEGER or OBJECT IDENTIFIER (or a type derived +from either of those by tagging). In that form, the actual +type is determined by the value of the other component, +either in the registration of the object identifier value, +or in a table of integer values. + +Example: X.509's AlgorithmIdentifier type has a component of +type ANY: + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL } + +Here the actual type of the parameter component depends on +the value of the algorithm component. The actual type would +be defined in the registration of object identifier values +for the algorithm component. + +BER encoding. Same as the BER encoding of the actual value. + +Example: The BER encoding of the value of the parameter +component is the BER encoding of the value of the actual +type as defined in the registration of object identifier +values for the algorithm component. + +DER encoding. Same as the DER encoding of the actual value. + + +5.4 BIT STRING + +The BIT STRING type denotes an arbitrary string of bits +(ones and zeroes). A BIT STRING value can have any length, +including zero. This type is a string type. + +The BIT STRING type is used for digital signatures on +extended certificates in PKCS #6's ExtendedCertificate type, +for digital signatures on certificates in X.509's +Certificate type, and for public keys in certificates in +X.509's SubjectPublicKeyInfo type. + +ASN.1 notation: + +BIT STRING + +Example: X.509's SubjectPublicKeyInfo type has a component +of type BIT STRING: + +SubjectPublicKeyInfo ::= SEQUENCE { + algorithm AlgorithmIdentifier, + publicKey BIT STRING } + +BER encoding. Primitive or constructed. In a primitive +encoding, the first contents octet gives the number of bits +by which the length of the bit string is less than the next +multiple of eight (this is called the "number of unused +bits"). The second and following contents octets give the +value of the bit string, converted to an octet string. The +conversion process is as follows: + + 1. The bit string is padded after the last bit with + zero to seven bits of any value to make the length + of the bit string a multiple of eight. If the + length of the bit string is a multiple of eight + already, no padding is done. + + 2. The padded bit string is divided into octets. The + first eight bits of the padded bit string become + the first octet, bit 8 to bit 1, and so on through + the last eight bits of the padded bit string. + +In a constructed encoding, the contents octets give the +concatenation of the BER encodings of consecutive substrings +of the bit string, where each substring except the last has +a length that is a multiple of eight bits. + +Example: The BER encoding of the BIT STRING value +"011011100101110111" can be any of the following, among +others, depending on the choice of padding bits, the form of +length octets, and whether the encoding is primitive or +constructed: + +03 04 06 6e 5d c0 DER encoding + +03 04 06 6e 5d e0 padded with "100000" + +03 81 04 06 6e 5d c0 long form of length octets + +23 09 constructed encoding: "0110111001011101" + "11" + 03 03 00 6e 5d + 03 02 06 c0 + +DER encoding. Primitive. The contents octects are as for a +primitive BER encoding, except that the bit string is padded +with zero-valued bits. + +Example: The DER encoding of the BIT STRING value +"011011100101110111" is + +03 04 06 6e 5d c0 + + +5.5 CHOICE + +The CHOICE type denotes a union of one or more alternatives. + +The CHOICE type is used to represent the union of an +extended certificate and an X.509 certificate in PKCS #7's +ExtendedCertificateOrCertificate type. + +ASN.1 notation: + +CHOICE { + [identifier1] Type1, + ..., + [identifiern] Typen } + +where identifier1 , ..., identifiern are optional, distinct +identifiers for the alternatives, and Type1, ..., Typen are +the types of the alternatives. The identifiers are primarily +for documentation; they do not affect values of the type or +their encodings in any way. + +The types must have distinct tags. This requirement is +typically satisfied with explicit or implicit tagging on +some of the alternatives. + +Example: PKCS #7's ExtendedCertificateOrCertificate type is +a CHOICE type: + +ExtendedCertificateOrCertificate ::= CHOICE { + certificate Certificate, -- X.509 + extendedCertificate [0] IMPLICIT ExtendedCertificate +} + +Here the identifiers for the alternatives are certificate +and extendedCertificate, and the types of the alternatives +are Certificate and [0] IMPLICIT ExtendedCertificate. + +BER encoding. Same as the BER encoding of the chosen +alternative. The fact that the alternatives have distinct +tags makes it possible to distinguish between their BER +encodings. + +Example: The identifier octets for the BER encoding are 30 +if the chosen alternative is certificate, and a0 if the +chosen alternative is extendedCertificate. + +DER encoding. Same as the DER encoding of the chosen +alternative. + + +5.6 IA5String + +The IA5String type denotes an arbtrary string of IA5 +characters. IA5 stands for International Alphabet 5, which +is the same as ASCII. The character set includes non- +printing control characters. An IA5String value can have any +length, including zero. This type is a string type. + +The IA5String type is used in PKCS #9's electronic-mail +address, unstructured-name, and unstructured-address +attributes. + +ASN.1 notation: + +IA5String + +BER encoding. Primitive or constructed. In a primitive +encoding, the contents octets give the characters in the IA5 +string, encoded in ASCII. In a constructed encoding, the +contents octets give the concatenation of the BER encodings +of consecutive substrings of the IA5 string. + +Example: The BER encoding of the IA5String value +"test1@rsa.com" can be any of the following, among others, +depending on the form of length octets and whether the +encoding is primitive or constructed: + +16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d DER encoding + +16 81 0d long form of length octets + 74 65 73 74 31 40 72 73 61 2e 63 6f 6d + +36 13 constructed encoding: "test1" + "@" + "rsa.com" + 16 05 74 65 73 74 31 + 16 01 40 + 16 07 72 73 61 2e 63 6f 6d + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + +Example: The DER encoding of the IA5String value +"test1@rsa.com" is + +16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d + + +5.7 INTEGER + +The INTEGER type denotes an arbitrary integer. INTEGER +values can be positive, negative, or zero, and can have any +magnitude. + +The INTEGER type is used for version numbers throughout +PKCS, cryptographic values such as modulus, exponent, and +primes in PKCS #1's RSAPublicKey and RSAPrivateKey types and +PKCS #3's DHParameter type, a message-digest iteration count +in PKCS #5's PBEParameter type, and version numbers and +serial numbers in X.509's Certificate type. + +ASN.1 notation: + +INTEGER [{ identifier1(value1) ... identifiern(valuen) }] + +where identifier1, ..., identifiern are optional distinct +identifiers and value1, ..., valuen are optional integer +values. The identifiers, when present, are associated with +values of the type. + +Example: X.509's Version type is an INTEGER type with +identified values: + +Version ::= INTEGER { v1988(0) } + +The identifier v1988 is associated with the value 0. X.509's +Certificate type uses the identifier v1988 to give a default +value of 0 for the version component: + +Certificate ::= ... + version Version DEFAULT v1988, +... + +BER encoding. Primitive. Contents octets give the value of +the integer, base 256, in two's complement form, most +significant digit first, with the minimum number of octets. +The value 0 is encoded as a single 00 octet. + +Some example BER encodings (which also happen to be DER +encodings) are given in Table 3. + + Integer BER encoding + value + 0 02 01 00 + 127 02 01 7F + 128 02 02 00 80 + 256 02 02 01 00 + -128 02 01 80 + -129 02 02 FF 7F + + Table 3. Example BER encodings of INTEGER values. + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + + +5.8 NULL + +The NULL type denotes a null value. + +The NULL type is used for algorithm parameters in several +places in PKCS. + +ASN.1 notation: + +NULL + +BER encoding. Primitive. Contents octets are empty. + +Example: The BER encoding of a NULL value can be either of +the following, as well as others, depending on the form of +the length octets: + +05 00 + +05 81 00 + +DER encoding. Primitive. Contents octets are empty; the DER +encoding of a NULL value is always 05 00. + + +5.9 OBJECT IDENTIFIER + +The OBJECT IDENTIFIER type denotes an object identifier, a +sequence of integer components that identifies an object +such as an algorithm, an attribute type, or perhaps a +registration authority that defines other object +identifiers. An OBJECT IDENTIFIER value can have any number +of components, and components can generally have any +nonnegative value. This type is a non-string type. + +OBJECT IDENTIFIER values are given meanings by registration +authorities. Each registration authority is responsible for +all sequences of components beginning with a given sequence. +A registration authority typically delegates responsibility +for subsets of the sequences in its domain to other +registration authorities, or for particular types of object. +There are always at least two components. + +The OBJECT IDENTIFIER type is used to identify content in +PKCS #7's ContentInfo type, to identify algorithms in +X.509's AlgorithmIdentifier type, and to identify attributes +in X.501's Attribute and AttributeValueAssertion types. The +Attribute type is used by PKCS #6, #7, #8, #9, and #10, and +the AttributeValueAssertion type is used in X.501 +distinguished names. OBJECT IDENTIFIER values are defined +throughout PKCS. + +ASN.1 notation: + +OBJECT IDENTIFIER + +The ASN.1 notation for values of the OBJECT IDENTIFIER type +is + +{ [identifier] component1 ... componentn } + +componenti = identifieri | identifieri (valuei) | valuei + +where identifier, identifier1, ..., identifiern are +identifiers, and value1, ..., valuen are optional integer +values. + +The form without identifier is the "complete" value with all +its components; the form with identifier abbreviates the +beginning components with another object identifier value. +The identifiers identifier1, ..., identifiern are intended +primarily for documentation, but they must correspond to the +integer value when both are present. These identifiers can +appear without integer values only if they are among a small +set of identifiers defined in X.208. + +Example: The following values both refer to the object +identifier assigned to RSA Data Security, Inc.: + +{ iso(1) member-body(2) 840 113549 } +{ 1 2 840 113549 } + +(In this example, which gives ASN.1 value notation, the +object identifier values are decimal, not hexadecimal.) +Table 4 gives some other object identifier values and their +meanings. + + Object identifier value Meaning + { 1 2 } ISO member bodies + { 1 2 840 } US (ANSI) + { 1 2 840 113549 } RSA Data Security, Inc. + { 1 2 840 113549 1 } RSA Data Security, Inc. PKCS + { 2 5 } directory services (X.500) + { 2 5 8 } directory services-algorithms + + Table 4. Some object identifier values and their meanings. + +BER encoding. Primitive. Contents octets are as follows, +where value1, ..., valuen denote the integer values of the +components in the complete object identifier: + + 1. The first octet has value 40 * value1 + value2. + (This is unambiguous, since value1 is limited to + values 0, 1, and 2; value2 is limited to the range + 0 to 39 when value1 is 0 or 1; and, according to + X.208, n is always at least 2.) + + 2. The following octets, if any, encode value3, ..., + valuen. Each value is encoded base 128, most + significant digit first, with as few digits as + possible, and the most significant bit of each + octet except the last in the value's encoding set + to "1." + +Example: The first octet of the BER encoding of RSA Data +Security, Inc.'s object identifier is 40 * 1 + 2 = 42 = +2a16. The encoding of 840 = 6 * 128 + 4816 is 86 48 and the +encoding of 113549 = 6 * 1282 + 7716 * 128 + d16 is 86 f7 +0d. This leads to the following BER encoding: + +06 06 2a 86 48 86 f7 0d + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + + +5.10 OCTET STRING + +The OCTET STRING type denotes an arbitrary string of octets +(eight-bit values). An OCTET STRING value can have any +length, including zero. This type is a string type. + +The OCTET STRING type is used for salt values in PKCS #5's +PBEParameter type, for message digests, encrypted message +digests, and encrypted content in PKCS #7, and for private +keys and encrypted private keys in PKCS #8. + +ASN.1 notation: + +OCTET STRING [SIZE ({size | size1..size2})] + +where size, size1, and size2 are optional size constraints. +In the OCTET STRING SIZE (size) form, the octet string must +have size octets. In the OCTET STRING SIZE (size1..size2) +form, the octet string must have between size1 and size2 +octets. In the OCTET STRING form, the octet string can have +any size. + +Example: PKCS #5's PBEParameter type has a component of type +OCTET STRING: + +PBEParameter ::= SEQUENCE { + salt OCTET STRING SIZE(8), + iterationCount INTEGER } + +Here the size of the salt component is always eight octets. + +BER encoding. Primitive or constructed. In a primitive +encoding, the contents octets give the value of the octet +string, first octet to last octet. In a constructed +encoding, the contents octets give the concatenation of the +BER encodings of substrings of the OCTET STRING value. + +Example: The BER encoding of the OCTET STRING value 01 23 45 +67 89 ab cd ef can be any of the following, among others, +depending on the form of length octets and whether the +encoding is primitive or constructed: + +04 08 01 23 45 67 89 ab cd ef DER encoding + +04 81 08 01 23 45 67 89 ab cd ef long form of length octets + +24 0c constructed encoding: 01 ... 67 + 89 ... ef + 04 04 01 23 45 67 + 04 04 89 ab cd ef + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + +Example: The BER encoding of the OCTET STRING value 01 23 45 +67 89 ab cd ef is + +04 08 01 23 45 67 89 ab cd ef + + +5.11 PrintableString + +The PrintableString type denotes an arbitrary string of +printable characters from the following character set: + + A, B, ..., Z + a, b, ..., z + 0, 1, ..., 9 + (space) ' ( ) + , - . / : = ? + +This type is a string type. + +The PrintableString type is used in PKCS #9's challenge- +password and unstructuerd-address attributes, and in several +X.521 distinguished names attributes. + +ASN.1 notation: + +PrintableString + +BER encoding. Primitive or constructed. In a primitive +encoding, the contents octets give the characters in the +printable string, encoded in ASCII. In a constructed +encoding, the contents octets give the concatenation of the +BER encodings of consecutive substrings of the string. + +Example: The BER encoding of the PrintableString value "Test +User 1" can be any of the following, among others, depending +on the form of length octets and whether the encoding is +primitive or constructed: + +13 0b 54 65 73 74 20 55 73 65 72 20 31 DER encoding + +13 81 0b long form of length octets + 54 65 73 74 20 55 73 65 72 20 31 + +33 0f constructed encoding: "Test " + "User 1" + 13 05 54 65 73 74 20 + 13 06 55 73 65 72 20 31 + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + +Example: The DER encoding of the PrintableString value "Test +User 1" is + +13 0b 54 65 73 74 20 55 73 65 72 20 31 + + +5.12 SEQUENCE + +The SEQUENCE type denotes an ordered collection of one or +more types. + +The SEQUENCE type is used throughout PKCS and related +standards. + +ASN.1 notation: + +SEQUENCE { + [identifier1] Type1 [{OPTIONAL | DEFAULT value1}], + ..., + [identifiern] Typen [{OPTIONAL | DEFAULT valuen}]} + +where identifier1 , ..., identifiern are optional, distinct +identifiers for the components, Type1, ..., Typen are the +types of the components, and value1, ..., valuen are optional +default values for the components. The identifiers are +primarily for documentation; they do not affect values of +the type or their encodings in any way. + +The OPTIONAL qualifier indicates that the value of a +component is optional and need not be present in the +sequence. The DEFAULT qualifier also indicates that the +value of a component is optional, and assigns a default +value to the component when the component is absent. + +The types of any consecutive series of components with the +OPTIONAL or DEFAULT qualifier, as well as of any component +immediately following that series, must have distinct tags. +This requirement is typically satisfied with explicit or +implicit tagging on some of the components. + +Example: X.509's Validity type is a SEQUENCE type with two +components: + +Validity ::= SEQUENCE { + start UTCTime, + end UTCTime } + +Here the identifiers for the components are start and end, +and the types of the components are both UTCTime. + +BER encoding. Constructed. Contents octets are the +concatenation of the BER encodings of the values of the +components of the sequence, in order of definition, with the +following rules for components with the OPTIONAL and DEFAULT +qualifiers: + + o if the value of a component with the OPTIONAL or + DEFAULT qualifier is absent from the sequence, + then the encoding of that component is not + included in the contents octets + + o if the value of a component with the DEFAULT + qualifier is the default value, then the encoding + of that component may or may not be included in + the contents octets + +DER encoding. Constructed. Contents octets are the same as +the BER encoding, except that if the value of a component +with the DEFAULT qualifier is the default value, the +encoding of that component is not included in the contents +octets. + + +5.13 SEQUENCE OF + +The SEQUENCE OF type denotes an ordered collection of zero +or more occurrences of a given type. + +The SEQUENCE OF type is used in X.501 distinguished names. + +ASN.1 notation: + +SEQUENCE OF Type + +where Type is a type. + +Example: X.501's RDNSequence type consists of zero or more +occurences of the RelativeDistinguishedName type, most +significant occurrence first: + +RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + +BER encoding. Constructed. Contents octets are the +concatenation of the BER encodings of the values of the +occurrences in the collection, in order of occurence. + +DER encoding. Constructed. Contents octets are the +concatenation of the DER encodings of the values of the +occurrences in the collection, in order of occurence. + + +5.14 SET + +The SET type denotes an unordered collection of one or more +types. + +The SET type is not used in PKCS. + +ASN.1 notation: + +SET { + [identifier1] Type1 [{OPTIONAL | DEFAULT value1}], + ..., + [identifiern] Typen [{OPTIONAL | DEFAULT valuen}]} + +where identifier1, ..., identifiern are optional, distinct +identifiers for the components, Type1, ..., Typen are the +types of the components, and value1, ..., valuen are +optional default values for the components. The identifiers +are primarily for documentation; they do not affect values +of the type or their encodings in any way. + +The OPTIONAL qualifier indicates that the value of a +component is optional and need not be present in the set. +The DEFAULT qualifier also indicates that the value of a +component is optional, and assigns a default value to the +component when the component is absent. + +The types must have distinct tags. This requirement is +typically satisfied with explicit or implicit tagging on +some of the components. + +BER encoding. Constructed. Contents octets are the +concatenation of the BER encodings of the values of the +components of the set, in any order, with the following +rules for components with the OPTIONAL and DEFAULT +qualifiers: + + o if the value of a component with the OPTIONAL or + DEFAULT qualifier is absent from the set, then the + encoding of that component is not included in the + contents octets + + o if the value of a component with the DEFAULT + qualifier is the default value, then the encoding + of that component may or may not be included in + the contents octets + +DER encoding. Constructed. Contents octets are the same as +for the BER encoding, except that: + + 1. If the value of a component with the DEFAULT + qualifier is the default value, the encoding of + that component is not included. + + 2. There is an order to the components, namely + ascending order by tag. + + +5.15 SET OF + +The SET OF type denotes an unordered collection of zero or +more occurrences of a given type. + +The SET OF type is used for sets of attributes in PKCS #6, +#7, #8, #9 and #10, for sets of message-digest algorithm +identifiers, signer information, and recipient information +in PKCS #7, and in X.501 distinguished names. + +ASN.1 notation: + +SET OF Type + +where Type is a type. + +Example: X.501's RelativeDistinguishedName type consists of +zero or more occurrences of the AttributeValueAssertion +type, where the order is unimportant: + +RelativeDistinguishedName ::= + SET OF AttributeValueAssertion + +BER encoding. Constructed. Contents octets are the +concatenation of the BER encodings of the values of the +occurrences in the collection, in any order. + +DER encoding. Constructed. Contents octets are the same as +for the BER encoding, except that there is an order, namely +ascending lexicographic order of BER encoding. Lexicographic +comparison of two different BER encodings is done as +follows: Logically pad the shorter BER encoding after the +last octet with dummy octets that are smaller in value than +any normal octet. Scan the BER encodings from left to right +until a difference is found. The smaller-valued BER encoding +is the one with the smaller-valued octet at the point of +difference. + + +5.16 T61String + +The T61String type denotes an arbtrary string of T.61 +characters. T.61 is an eight-bit extension to the ASCII +character set. Special "escape" sequences specify the +interpretation of subsequent character values as, for +example, Japanese; the initial interpretation is Latin. The +character set includes non-printing control characters. The +T61String type allows only the Latin and Japanese character +interepretations, and implementors' agreements for directory +names exclude control characters [NIST92]. A T61String value +can have any length, including zero. This type is a string +type. + +The T61String type is used in PKCS #9's unstructured-address +and challenge-password attributes, and in several X.521 +attributes. + +ASN.1 notation: + +T61String + +BER encoding. Primitive or constructed. In a primitive +encoding, the contents octets give the characters in the +T.61 string, encoded in ASCII. In a constructed encoding, +the contents octets give the concatenation of the BER +encodings of consecutive substrings of the T.61 string. + +Example: The BER encoding of the T61String value "cl'es +publiques" (French for "public keys") can be any of the +following, among others, depending on the form of length +octets and whether the encoding is primitive or constructed: + +14 0f DER encoding + 63 6c c2 65 73 20 70 75 62 6c 69 71 75 65 73 + +14 81 0f long form of length octets + 63 6c c2 65 73 20 70 75 62 6c 69 71 75 65 73 + +34 15 constructed encoding: "cl'es" + " " + "publiques" + 14 05 63 6c c2 65 73 + 14 01 20 + 14 09 70 75 62 6c 69 71 75 65 73 + +The eight-bit character c2 is a T.61 prefix that adds an +acute accent (') to the next character. + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + +Example: The DER encoding of the T61String value "cl'es +publiques" is + +14 0f 63 6c c2 65 73 20 70 75 62 6c 69 71 75 65 73 + + +5.17 UTCTime + +The UTCTime type denotes a "coordinated universal time" or +Greenwich Mean Time (GMT) value. A UTCTime value includes +the local time precise to either minutes or seconds, and an +offset from GMT in hours and minutes. It takes any of the +following forms: + +YYMMDDhhmmZ +YYMMDDhhmm+hh'mm' +YYMMDDhhmm-hh'mm' +YYMMDDhhmmssZ +YYMMDDhhmmss+hh'mm' +YYMMDDhhmmss-hh'mm' + +where: + + YY is the least significant two digits of the year + + MM is the month (01 to 12) + + DD is the day (01 to 31) + + hh is the hour (00 to 23) + + mm are the minutes (00 to 59) + + ss are the seconds (00 to 59) + + Z indicates that local time is GMT, + indicates that + local time is later than GMT, and - indicates that + local time is earlier than GMT + + hh' is the absolute value of the offset from GMT in + hours + + mm' is the absolute value of the offset from GMT in + minutes + +This type is a string type. + +The UTCTime type is used for signing times in PKCS #9's +signing-time attribute and for certificate validity periods +in X.509's Validity type. + +ASN.1 notation: + +UTCTime + +BER encoding. Primitive or constructed. In a primitive +encoding, the contents octets give the characters in the +string, encoded in ASCII. In a constructed encoding, the +contents octets give the concatenation of the BER encodings +of consecutive substrings of the string. (The constructed +encoding is not particularly interesting, since UTCTime +values are so short, but the constructed encoding is +permitted.) + +Example: The time this sentence was originally written was +4:45:40 p.m. Pacific Daylight Time on May 6, 1991, which can +be represented with either of the following UTCTime values, +among others: + +"910506164540-0700" + +"910506234540Z" + +These values have the following BER encodings, among others: + +17 0d 39 31 30 35 30 36 32 33 34 35 34 30 5a + +17 11 39 31 30 35 30 36 31 36 34 35 34 30 2D 30 37 30 + 30 + +DER encoding. Primitive. Contents octets are as for a +primitive BER encoding. + + +6. An example + +This section gives an example of ASN.1 notation and DER +encoding: the X.501 type Name. + + +6.1 Abstract notation + +This section gives the ASN.1 notation for the X.501 type +Name. + +Name ::= CHOICE { + RDNSequence } + +RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + +RelativeDistinguishedName ::= + SET OF AttributeValueAssertion + +AttributeValueAssertion ::= SEQUENCE { + AttributeType, + AttributeValue } + +AttributeType ::= OBJECT IDENTIFIER + +AttributeValue ::= ANY + +The Name type identifies an object in an X.500 directory. +Name is a CHOICE type consisting of one alternative: +RDNSequence. (Future revisions of X.500 may have other +alternatives.) + +The RDNSequence type gives a path through an X.500 directory +tree starting at the root. RDNSequence is a SEQUENCE OF type +consisting of zero or more occurences of +RelativeDistinguishedName. + +The RelativeDistinguishedName type gives a unique name to an +object relative to the object superior to it in the +directory tree. RelativeDistinguishedName is a SET OF type +consisting of zero or more occurrences of +AttributeValueAssertion. + +The AttributeValueAssertion type assigns a value to some +attribute of a relative distinguished name, such as country +name or common name. AttributeValueAssertion is a SEQUENCE +type consisting of two components, an AttributeType type and +an AttributeValue type. + +The AttributeType type identifies an attribute by object +identifier. The AttributeValue type gives an arbitrary +attribute value. The actual type of the attribute value is +determined by the attribute type. + + +6.2 DER encoding + +This section gives an example of a DER encoding of a value +of type Name, working from the bottom up. + +The name is that of the Test User 1 from the PKCS examples +[Kal93]. The name is represented by the following path: + + (root) + | + countryName = "US" + | + organizationName = "Example Organization" + | + commonName = "Test User 1" + +Each level corresponds to one RelativeDistinguishedName +value, each of which happens for this name to consist of one +AttributeValueAssertion value. The AttributeType value is +before the equals sign, and the AttributeValue value (a +printable string for the given attribute types) is after the +equals sign. + +The countryName, organizationName, and commonUnitName are +attribute types defined in X.520 as: + +attributeType OBJECT IDENTIFIER ::= + { joint-iso-ccitt(2) ds(5) 4 } + +countryName OBJECT IDENTIFIER ::= { attributeType 6 } +organizationName OBJECT IDENTIFIER ::= + { attributeType 10 } +commonUnitName OBJECT IDENTIFIER ::= + { attributeType 3 } + + +6.2.1 AttributeType + +The three AttributeType values are OCTET STRING values, so +their DER encoding follows the primitive, definite-length +method: + +06 03 55 04 06 countryName + +06 03 55 04 0a organizationName + +06 03 55 04 03 commonName + +The identifier octets follow the low-tag form, since the tag +is 6 for OBJECT IDENTIFIER. Bits 8 and 7 have value "0," +indicating universal class, and bit 6 has value "0," +indicating that the encoding is primitive. The length octets +follow the short form. The contents octets are the +concatenation of three octet strings derived from +subidentifiers (in decimal): 40 * 2 + 5 = 85 = 5516; 4; and +6, 10, or 3. + + +6.2.2 AttributeValue + +The three AttributeValue values are PrintableString values, +so their encodings follow the primitive, definite-length +method: + +13 02 55 53 "US" + +13 14 "Example Organization" + 45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 + 74 69 6f 6e + +13 0b "Test User 1" + 54 65 73 74 20 55 73 65 72 20 31 + +The identifier octets follow the low-tag-number form, since +the tag for PrintableString, 19 (decimal), is between 0 and +30. Bits 8 and 7 have value "0" since PrintableString is in +the universal class. Bit 6 has value "0" since the encoding +is primitive. The length octets follow the short form, and +the contents octets are the ASCII representation of the +attribute value. + + +6.2.3 AttributeValueAssertion + +The three AttributeValueAssertion values are SEQUENCE +values, so their DER encodings follow the constructed, +definite-length method: + +30 09 countryName = "US" + 06 03 55 04 06 + 13 02 55 53 + +30 1b organizationName = "Example Organizaiton" + 06 03 55 04 0a + 13 14 ... 6f 6e + +30 12 commonName = "Test User 1" + 06 03 55 04 0b + 13 0b ... 20 31 + +The identifier octets follow the low-tag-number form, since +the tag for SEQUENCE, 16 (decimal), is between 0 and 30. +Bits 8 and 7 have value "0" since SEQUENCE is in the +universal class. Bit 6 has value "1" since the encoding is +constructed. The length octets follow the short form, and +the contents octets are the concatenation of the DER +encodings of the attributeType and attributeValue +components. + + +6.2.4 RelativeDistinguishedName + +The three RelativeDistinguishedName values are SET OF +values, so their DER encodings follow the constructed, +definite-length method: + +31 0b + 30 09 ... 55 53 + +31 1d + 30 1b ... 6f 6e + +31 14 + 30 12 ... 20 31 + +The identifier octets follow the low-tag-number form, since +the tag for SET OF, 17 (decimal), is between 0 and 30. Bits +8 and 7 have value "0" since SET OF is in the universal +class Bit 6 has value "1" since the encoding is constructed. +The lengths octets follow the short form, and the contents +octets are the DER encodings of the respective +AttributeValueAssertion values, since there is only one +value in each set. + + +6.2.5 RDNSequence + +The RDNSequence value is a SEQUENCE OF value, so its DER +encoding follows the constructed, definite-length method: + +30 42 + 31 0b ... 55 53 + 31 1d ... 6f 6e + 31 14 ... 20 31 + +The identifier octets follow the low-tag-number form, since +the tag for SEQUENCE OF, 16 (decimal), is between 0 and 30. +Bits 8 and 7 have value "0" since SEQUENCE OF is in the +universal class. Bit 6 has value "1" since the encoding is +constructed. The lengths octets follow the short form, and +the contents octets are the concatenation of the DER +encodings of the three RelativeDistinguishedName values, in +order of occurrence. + + +6.2.6 Name + +The Name value is a CHOICE value, so its DER encoding is the +same as that of the RDNSequence value: + +30 42 + 31 0b + 30 09 + 06 03 55 04 06 attributeType = countryName + 13 02 55 53 attributeValue = "US" + 31 1d + 30 1b + 06 03 55 04 0a attributeType = organizationName + 13 14 attributeValue = "Example Organization" + 45 78 61 6d 70 6c 65 20 4f 72 67 61 6e 69 7a 61 + 74 69 6f 6e + + 31 14 + 30 12 + 06 03 55 04 03 attributeType = commonName + 13 0b attributeValue = "Test User 1" + 54 65 73 74 20 55 73 65 72 20 31 + + +References + +PKCS #1 RSA Laboratories. PKCS #1: RSA Encryption + Standard. Version 1.5, November 1993. + +PKCS #3 RSA Laboratories. PKCS #3: Diffie-Hellman Key- + Agreement Standard. Version 1.4, November 1993. + +PKCS #5 RSA Laboratories. PKCS #5: Password-Based + Encryption Standard. Version 1.5, November 1993. + +PKCS #6 RSA Laboratories. PKCS #6: Extended-Certificate + Syntax Standard. Version 1.5, November 1993. + +PKCS #7 RSA Laboratories. PKCS #7: Cryptographic Message + Syntax Standard. Version 1.5, November 1993. + +PKCS #8 RSA Laboratories. PKCS #8: Private-Key Information + Syntax Standard. Version 1.2, November 1993. + +PKCS #9 RSA Laboratories. PKCS #9: Selected Attribute + Types. Version 1.1, November 1993. + +PKCS #10 RSA Laboratories. PKCS #10: Certification Request + Syntax Standard. Version 1.0, November 1993. + +X.200 CCITT. Recommendation X.200: Reference Model of + Open Systems Interconnection for CCITT + Applications. 1984. + +X.208 CCITT. Recommendation X.208: Specification of + Abstract Syntax Notation One (ASN.1). 1988. + +X.209 CCITT. Recommendation X.209: Specification of + Basic Encoding Rules for Abstract Syntax Notation + One (ASN.1). 1988. + +X.500 CCITT. Recommendation X.500: The + Directory--Overview of Concepts, Models and + Services. 1988. + +X.501 CCITT. Recommendation X.501: The Directory-- + Models. 1988. + +X.509 CCITT. Recommendation X.509: The Directory-- + Authentication Framework. 1988. + +X.520 CCITT. Recommendation X.520: The Directory-- + Selected Attribute Types. 1988. + +[Kal93] Burton S. Kaliski Jr. Some Examples of the PKCS + Standards. RSA Laboratories, November 1993. + +[NIST92] NIST. Special Publication 500-202: Stable + Implementation Agreements for Open Systems + Interconnection Protocols. Part 11 (Directory + Services Protocols). December 1992. + + +Revision history + + +June 3, 1991 version + +The June 3, 1991 version is part of the initial public +release of PKCS. It was published as NIST/OSI Implementors' +Workshop document SEC-SIG-91-17. + + +November 1, 1993 version + +The November 1, 1993 version incorporates several editorial +changes, including the addition of a revision history. It is +updated to be consistent with the following versions of the +PKCS documents: + + PKCS #1: RSA Encryption Standard. Version 1.5, November + 1993. + + PKCS #3: Diffie-Hellman Key-Agreement Standard. Version + 1.4, November 1993. + + PKCS #5: Password-Based Encryption Standard. Version + 1.5, November 1993. + + PKCS #6: Extended-Certificate Syntax Standard. Version + 1.5, November 1993. + + PKCS #7: Cryptographic Message Syntax Standard. Version + 1.5, November 1993. + + PKCS #8: Private-Key Information Syntax Standard. + Version 1.2, November 1993. + + PKCS #9: Selected Attribute Types. Version 1.1, + November 1993. + + PKCS #10: Certification Request Syntax Standard. + Version 1.0, November 1993. + +The following substantive changes were made: + + Section 5: Description of T61String type is added. + + Section 6: Names are changed, consistent with other + PKCS examples. + + +Author's address + +Burton S. Kaliski Jr., Ph.D. +Chief Scientist +RSA Laboratories (415) 595-7703 +100 Marine Parkway (415) 595-4126 (fax) +Redwood City, CA 94065 USA burt@rsa.com diff --git a/tcllib/modules/asn/pkgIndex.tcl b/tcllib/modules/asn/pkgIndex.tcl new file mode 100644 index 0000000..3cbafd6 --- /dev/null +++ b/tcllib/modules/asn/pkgIndex.tcl @@ -0,0 +1,4 @@ +# Tcl package index file, version 1.1 + +if {![package vsatisfies [package provide Tcl] 8.4]} {return} +package ifneeded asn 0.8.4 [list source [file join $dir asn.tcl]] |