asn.tcl

Go to the documentation of this file.
00001 /* -----------------------------------------------------------------------------*/
00002 /*    Copyright (C) 1999-2004 Jochen C. Loewer (loewerj@web.de)*/
00003 /*    Copyright (C) 2004-2007 Michael Schlenker (mic42@users.sourceforge.net)*/
00004 /* -----------------------------------------------------------------------------*/
00005 /*    */
00006 /*    A partial ASN decoder/encoder implementation in plain Tcl. */
00007 /* */
00008 /*    See ASN.1 (X.680) and BER (X.690).*/
00009 /*    See 'asn_ber_intro.txt' in this directory.*/
00010 /* */
00011 /*    This software is copyrighted by Jochen C. Loewer (loewerj@web.de). The */
00012 /*    following terms apply to all files associated with the software unless */
00013 /*    explicitly disclaimed in individual files.*/
00014 /* */
00015 /*    The authors hereby grant permission to use, copy, modify, distribute,*/
00016 /*    and license this software and its documentation for any purpose, provided*/
00017 /*    that existing copyright notices are retained in all copies and that this*/
00018 /*    notice is included verbatim in any distributions. No written agreement,*/
00019 /*    license, or royalty fee is required for any of the authorized uses.*/
00020 /*    Modifications to this software may be copyrighted by their authors*/
00021 /*    and need not follow the licensing terms described here, provided that*/
00022 /*    the new terms are clearly indicated on the first page of each file where*/
00023 /*    they apply.*/
00024 /*   */
00025 /*    IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY*/
00026 /*    FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES*/
00027 /*    ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY*/
00028 /*    DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE*/
00029 /*    POSSIBILITY OF SUCH DAMAGE.*/
00030 /* */
00031 /*    THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,*/
00032 /*    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,*/
00033 /*    FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE*/
00034 /*    IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE*/
00035 /*    NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR*/
00036 /*    MODIFICATIONS.*/
00037 /* */
00038 /*    written by Jochen Loewer*/
00039 /*    3 June, 1999*/
00040 /* */
00041 /*    $Id: asn.tcl,v 1.17 2007/04/21 12:35:14 mic42 Exp $*/
00042 /* */
00043 /* -----------------------------------------------------------------------------*/
00044 
00045 /*  needed for using wide()*/
00046 package require Tcl 8.4
00047 
00048 namespace asn {
00049     /*  Encoder commands*/
00050     namespace export \
00051         asnSequence \
00052     asnSequenceFromList \
00053         asnSet \
00054     asnSetFromList \
00055         asnApplicationConstr \
00056         asnApplication \
00057     asnContext\
00058     asnContextConstr\
00059         asnChoice \
00060         asnChoiceConstr \
00061         asnInteger \
00062         asnEnumeration \
00063         asnBoolean \
00064         asnOctetString \
00065         asnNull    \
00066     asnUTCTime \
00067     asnNumericString \
00068         asnPrintableString \
00069         asnIA5String\
00070     asnBMPString\
00071     asnUTF8String\
00072         asnBitString \
00073         asnObjectIdentifer 
00074         
00075     /*  Decoder commands*/
00076     namespace export \
00077         asnGetResponse \
00078         asnGetInteger \
00079         asnGetEnumeration \
00080         asnGetOctetString \
00081         asnGetSequence \
00082         asnGetSet \
00083         asnGetApplication \
00084     asnGetNumericString \
00085         asnGetPrintableString \
00086         asnGetIA5String \
00087     asnGetBMPString \
00088     asnGetUTF8String \
00089         asnGetObjectIdentifier \
00090         asnGetBoolean \
00091         asnGetUTCTime \
00092         asnGetBitString \
00093         asnGetContext 
00094     
00095     /*  general BER utility commands    */
00096     namespace export \
00097         asnPeekByte  \
00098         asnGetLength \
00099         asnRetag     \
00100     asnPeekTag   \
00101     asnTag       
00102         
00103 }
00104 
00105 /* -----------------------------------------------------------------------------*/
00106 /*  Implementation notes:*/
00107 /* */
00108 /*  See the 'asn_ber_intro.txt' in this directory for an introduction*/
00109 /*  into BER/DER encoding of ASN.1 information. Bibliography information*/
00110 /* */
00111 /*    A Layman's Guide to a Subset of ASN.1, BER, and DER*/
00112 /* */
00113 /*    An RSA Laboratories Technical Note*/
00114 /*    Burton S. Kaliski Jr.*/
00115 /*    Revised November 1, 1993*/
00116 /* */
00117 /*    Supersedes June 3, 1991 version, which was also published as*/
00118 /*    NIST/OSI Implementors' Workshop document SEC-SIG-91-17.*/
00119 /*    PKCS documents are available by electronic mail to*/
00120 /*    <pkcs@rsa.com>.*/
00121 /* */
00122 /*    Copyright (C) 1991-1993 RSA Laboratories, a division of RSA*/
00123 /*    Data Security, Inc. License to copy this document is granted*/
00124 /*    provided that it is identified as "RSA Data Security, Inc.*/
00125 /*    Public-Key Cryptography Standards (PKCS)" in all material*/
00126 /*    mentioning or referencing this document.*/
00127 /*    003-903015-110-000-000*/
00128 /* */
00129 /* -----------------------------------------------------------------------------*/
00130 
00131 /* -----------------------------------------------------------------------------*/
00132 /*  asnLength : Encode some length data. Helper command.*/
00133 /* -----------------------------------------------------------------------------*/
00134 
00135 ret  ::asn::asnLength (type len) {
00136     
00137     if {$len < 0} {
00138         return -code error "Negative length octet requested"
00139     }
00140     if {$len < 128} {
00141         # short form: ISO X.690 8.1.3.4 
00142         return [binary format c $len]
00143     }
00144     # long form: ISO X.690 8.1.3.5
00145     # try to use a minimal encoding, 
00146     # even if not required by BER, but it is required by DER
00147     # take care for signed vs. unsigned issues
00148     if {$len < 256  } {
00149         return [binary format H2c 81 [expr {$len - 256}]]
00150     }
00151     if {$len < 32769} {
00152         # two octet signed value
00153         return [binary format H2S 82 $len]
00154     }
00155     if {$len < 65536} {
00156         return [binary format H2S 82 [expr {$len - 65536}]]
00157     }
00158     if {$len < 8388608} {
00159         # three octet signed value    
00160         return [binary format H2cS 83 [expr {$len >> 16}] [expr {($len & 0xFFFF) - 65536}]] 
00161     }    
00162     if {$len < 16777216} {
00163         # three octet signed value    
00164         return [binary format H2cS 83 [expr {($len >> 16) -256}] [expr {($len & 0xFFFF) -65536}]] 
00165     }
00166     if {$len < 2147483649} { 
00167         # four octet signed value
00168         return [binary format H2I 84 $len]
00169     }
00170     if {$len < 4294967296} {
00171         # four octet unsigned value
00172         return [binary format H2I 84 [expr {$len - 4294967296}]]
00173     }
00174     if {$len < 1099511627776} {
00175         # five octet unsigned value
00176         return [binary format H2 85][string range [binary format W $len] 3 end]  
00177     }
00178     if {$len < 281474976710656} {
00179         # six octet unsigned value
00180         return [binary format H2 86][string range [binary format W $len] 2 end]
00181     }
00182     if {$len < 72057594037927936} {
00183         # seven octet value
00184         return [binary format H2 87][string range [binary format W $len] 1 end]
00185     }
00186     
00187     # must be a 64-bit wide signed value
00188     return [binary format H2W 88 $len] 
00189 }
00190 
00191 /* -----------------------------------------------------------------------------*/
00192 /*  asnSequence : Assumes that the arguments are already ASN encoded.*/
00193 /* -----------------------------------------------------------------------------*/
00194 
00195 ret  ::asn::asnSequence (type args) {
00196     asnSequenceFromList $args
00197 }
00198 
00199 ret  ::asn::asnSequenceFromList (type lst) {
00200     # The sequence tag is 0x30. The length is arbitrary and thus full
00201     # length coding is required. The arguments have to be BER encoded
00202     # already. Constructed value, definite-length encoding.
00203 
00204     set out ""
00205     foreach part $lst {
00206         append out $part
00207     }
00208     set len [string length $out]
00209     return [binary format H2a*a$len 30 [asnLength $len] $out]
00210 }
00211 
00212 
00213 /* -----------------------------------------------------------------------------*/
00214 /*  asnSet : Assumes that the arguments are already ASN encoded.*/
00215 /* -----------------------------------------------------------------------------*/
00216 
00217 ret  ::asn::asnSet (type args) {
00218     asnSetFromList $args
00219 }
00220 
00221 ret  ::asn::asnSetFromList (type lst) {
00222     # The set tag is 0x31. The length is arbitrary and thus full
00223     # length coding is required. The arguments have to be BER encoded
00224     # already.
00225 
00226     set out ""
00227     foreach part $lst {
00228         append out $part
00229     }
00230     set len [string length $out]
00231     return [binary format H2a*a$len 31 [asnLength $len] $out]
00232 }
00233 
00234 
00235 /* -----------------------------------------------------------------------------*/
00236 /*  asnApplicationConstr*/
00237 /* -----------------------------------------------------------------------------*/
00238 
00239 ret  ::asn::asnApplicationConstr (type appNumber , type args) {
00240     # Packs the arguments into a constructed value with application tag.
00241 
00242     set out ""
00243     foreach part $args {
00244         append out $part
00245     }
00246     set code [expr {0x060 + $appNumber}]
00247     set len  [string length $out]
00248     return [binary format ca*a$len $code [asnLength $len] $out]
00249 }
00250 
00251 /* -----------------------------------------------------------------------------*/
00252 /*  asnApplication*/
00253 /* -----------------------------------------------------------------------------*/
00254 
00255 ret  ::asn::asnApplication (type appNumber , type data) {
00256     # Packs the arguments into a constructed value with application tag.
00257 
00258     set code [expr {0x040 + $appNumber}]
00259     set len  [string length $data]
00260     return [binary format ca*a$len $code [asnLength $len] $data]
00261 }
00262 
00263 /* -----------------------------------------------------------------------------*/
00264 /*  asnContextConstr*/
00265 /* -----------------------------------------------------------------------------*/
00266 
00267 ret  ::asn::asnContextConstr (type contextNumber , type args) {
00268     # Packs the arguments into a constructed value with application tag.
00269 
00270     set out ""
00271     foreach part $args {
00272         append out $part
00273     }
00274     set code [expr {0x0A0 + $contextNumber}]
00275     set len  [string length $out]
00276     return [binary format ca*a$len $code [asnLength $len] $out]
00277 }
00278 
00279 /* -----------------------------------------------------------------------------*/
00280 /*  asnContext*/
00281 /* -----------------------------------------------------------------------------*/
00282 
00283 ret  ::asn::asnContext (type contextNumber , type data) {
00284     # Packs the arguments into a constructed value with application tag.
00285     set code [expr {0x080 + $contextNumber}]
00286     set len  [string length $data]
00287     return [binary format ca*a$len $code [asnLength $len] $data]
00288 }
00289 /* -----------------------------------------------------------------------------*/
00290 /*  asnChoice*/
00291 /* -----------------------------------------------------------------------------*/
00292 
00293 ret  ::asn::asnChoice (type appNumber , type args) {
00294     # Packs the arguments into a choice construction.
00295 
00296     set out ""
00297     foreach part $args {
00298         append out $part
00299     }
00300     set code [expr {0x080 + $appNumber}]
00301     set len  [string length $out]
00302     return [binary format ca*a$len $code [asnLength $len] $out]
00303 }
00304 
00305 /* -----------------------------------------------------------------------------*/
00306 /*  asnChoiceConstr*/
00307 /* -----------------------------------------------------------------------------*/
00308 
00309 ret  ::asn::asnChoiceConstr (type appNumber , type args) {
00310     # Packs the arguments into a choice construction.
00311 
00312     set out ""
00313     foreach part $args {
00314         append out $part
00315     }
00316     set code [expr {0x0A0 + $appNumber}]
00317     set len  [string length $out]
00318     return [binary format ca*a$len $code [asnLength $len] $out]
00319 }
00320 
00321 /* -----------------------------------------------------------------------------*/
00322 /*  asnInteger : Encode integer value.*/
00323 /* -----------------------------------------------------------------------------*/
00324 
00325 ret  ::asn::asnInteger (type number) {
00326     asnIntegerOrEnum 02 $number
00327 }
00328 
00329 /* -----------------------------------------------------------------------------*/
00330 /*  asnEnumeration : Encode enumeration value.*/
00331 /* -----------------------------------------------------------------------------*/
00332 
00333 ret  ::asn::asnEnumeration (type number) {
00334     asnIntegerOrEnum 0a $number
00335 }
00336 
00337 /* -----------------------------------------------------------------------------*/
00338 /*  asnIntegerOrEnum : Common code for Integers and Enumerations*/
00339 /*                     No Bignum version, as we do not expect large Enums.*/
00340 /* -----------------------------------------------------------------------------*/
00341 
00342 ret  ::asn::asnIntegerOrEnum (type tag , type number) {
00343     # The integer tag is 0x02 , the Enum Tag 0x0a otherwise identical. 
00344     # The length is 1, 2, 3, or 4, coded in a
00345     # single byte. This can be done directly, no need to go through
00346     # asnLength. The value itself is written in big-endian.
00347 
00348     # Known bug/issue: The command cannot handle very wide integers, i.e.
00349     # anything above 8 bytes length. Use asnBignumInteger for those.
00350     
00351     # check if we really have an int
00352     set num $number
00353     incr num
00354     
00355     if {($number >= -128) && ($number < 128)} {
00356         return [binary format H2H2c $tag 01 $number]
00357     }
00358     if {($number >= -32768) && ($number < 32768)} {
00359         return [binary format H2H2S $tag 02 $number]
00360     }
00361     if {($number >= -8388608) && ($number < 8388608)} {
00362         set numberb [expr {$number & 0xFFFF}]
00363         set numbera [expr {($number >> 16) & 0xFF}]
00364         return [binary format H2H2cS $tag 03 $numbera $numberb]
00365     }
00366     if {($number >= -2147483648) && ($number < 2147483648)} {
00367         return [binary format H2H2I $tag 04 $number]
00368     }
00369     if {($number >= -549755813888) && ($number < 549755813888)} {
00370         set numberb [expr {$number & 0xFFFFFFFF}]
00371         set numbera [expr {($number >> 32) & 0xFF}]
00372         return [binary format H2H2cI $tag 05 $numbera $numberb]
00373     }
00374     if {($number >= -140737488355328) && ($number < 140737488355328)} {
00375         set numberb [expr {$number & 0xFFFFFFFF}]
00376         set numbera [expr {($number >> 32) & 0xFFFF}]
00377         return [binary format H2H2SI $tag 06 $numbera $numberb]        
00378     }
00379     if {($number >= -36028797018963968) && ($number < 36028797018963968)} {
00380         set numberc [expr {$number & 0xFFFFFFFF}]
00381         set numberb [expr {($number >> 32) & 0xFFFF}]
00382         set numbera [expr {($number >> 48) & 0xFF}]
00383         return [binary format H2H2cSI $tag 07 $numbera $numberb $numberc]        
00384     }    
00385     if {($number >= -9223372036854775808) && ($number <= 9223372036854775807)} {
00386         return [binary format H2H2W $tag 08 $number]
00387     }
00388     return -code error "Integer value to large to encode, use asnBigInteger" 
00389 }
00390 
00391 /* -----------------------------------------------------------------------------*/
00392 /*  asnBigInteger : Encode a long integer value using math::bignum*/
00393 /* -----------------------------------------------------------------------------*/
00394 
00395 ret  ::asn::asnBigInteger (type bignum) {
00396     # require math::bignum only if it is used
00397     package require math::bignum
00398     
00399     # this is a hack to check for bignum...
00400     if {[llength $bignum] < 2 || ([lindex $bignum 0] ne "bignum")} {
00401         return -code error "expected math::bignum value got \"$bignum\""
00402     }
00403     if {[math::bignum::sign $bignum]} {
00404         # generate two's complement form
00405         set bits [math::bignum::bits $bignum]
00406         set padding [expr {$bits % 8}]
00407         set len [expr {int(ceil($bits / 8.0))}]
00408         if {$padding == 0} {
00409             # we need a complete extra byte for the sign
00410             # unless this is a base 2 multiple
00411             set test [math::bignum::fromstr 0]
00412             math::bignum::setbit test [expr {$bits-1}]
00413             if {[math::bignum::ne [math::bignum::abs $bignum] $test]} {
00414                 incr len
00415             }
00416         }
00417         set exp [math::bignum::pow \
00418             [math::bignum::fromstr 256] \
00419             [math::bignum::fromstr $len]]
00420         set bignum [math::bignum::add $bignum $exp]
00421         set hex [math::bignum::tostr $bignum 16]
00422     } else {
00423         set bits [math::bignum::bits $bignum]
00424         if {($bits % 8) == 0 && $bits > 0} {
00425             set pad "00"
00426         } else {
00427             set pad ""
00428         }
00429         set hex $pad[math::bignum::tostr $bignum 16]
00430     }
00431     if {[string length $hex]%2} {
00432         set hex "0$hex"
00433     }
00434     set octets [expr {(([string length $hex]+1)/2)}]
00435     return [binary format H2a*H* 02 [asnLength $octets] $hex]   
00436 }
00437 
00438 
00439 /* -----------------------------------------------------------------------------*/
00440 /*  asnBoolean : Encode a boolean value.*/
00441 /* -----------------------------------------------------------------------------*/
00442 
00443 ret  ::asn::asnBoolean (type bool) {
00444     # The boolean tag is 0x01. The length is always 1, coded in
00445     # a single byte. This can be done directly, no need to go through
00446     # asnLength. The value itself is written in big-endian.
00447 
00448     return [binary format H2H2c 01 01 [expr {$bool ? 0x0FF : 0x0}]]
00449 }
00450 
00451 /* -----------------------------------------------------------------------------*/
00452 /*  asnOctetString : Encode a string of arbitrary bytes*/
00453 /* -----------------------------------------------------------------------------*/
00454 
00455 ret  ::asn::asnOctetString (type string) {
00456     # The octet tag is 0x04. The length is arbitrary, so we need
00457     # 'asnLength' for full coding of the length.
00458 
00459     set len [string length $string]
00460     return [binary format H2a*a$len 04 [asnLength $len] $string]
00461 }
00462 
00463 /* -----------------------------------------------------------------------------*/
00464 /*  asnNull : Encode a null value*/
00465 /* -----------------------------------------------------------------------------*/
00466 
00467 ret  ::asn::asnNull () {
00468     # Null has only one valid encoding
00469     return \x05\x00
00470 }
00471 
00472 /* -----------------------------------------------------------------------------*/
00473 /*  asnBitstring : Encode a Bit String value*/
00474 /* -----------------------------------------------------------------------------*/
00475 
00476 ret  ::asn::asnBitString (type bitstring) {
00477     # The bit string tag is 0x03.
00478     # Bit strings can be either simple or constructed
00479     # we always use simple encoding
00480     
00481     set bitlen [string length $bitstring]
00482     set padding [expr {(8 - ($bitlen % 8)) % 8}]
00483     set len [expr {($bitlen / 8) + 1}]
00484     if {$padding != 0} {incr len}
00485     
00486     return [binary format H2a*B* 03 [asnLength $len] $bitstring]    
00487 }
00488 
00489 /* -----------------------------------------------------------------------------*/
00490 /*  asnUTCTime : Encode an UTC time string*/
00491 /* -----------------------------------------------------------------------------*/
00492 
00493 ret  ::asn::asnUTCTime (type UTCtimestring) {
00494     # the utc time tag is 0x17.
00495     # 
00496     # BUG: we do not check the string for well formedness
00497     
00498     set ascii [encoding convertto ascii $UTCtimestring]
00499     set len [string length $ascii]
00500     return [binary format H2a*a* 17 [asnLength $len] $ascii]
00501 }
00502 
00503 /* -----------------------------------------------------------------------------*/
00504 /*  asnPrintableString : Encode a printable string*/
00505 /* -----------------------------------------------------------------------------*/
00506 namespace asn {
00507     variable nonPrintableChars {[^ A-Za-z0-9'()+,.:/?=-]}
00508 }   
00509 ret  ::asn::asnPrintableString (type string) {
00510     # the printable string tag is 0x13
00511     variable nonPrintableChars
00512     # it is basically a restricted ascii string
00513     if {[regexp $nonPrintableChars $string ]} {
00514         return -code error "Illegal character in PrintableString."
00515     }
00516     
00517     # check characters
00518     set ascii [encoding convertto ascii $string]
00519     return [asnEncodeString 13 $ascii]
00520 }
00521 
00522 /* -----------------------------------------------------------------------------*/
00523 /*  asnIA5String : Encode an Ascii String*/
00524 /* -----------------------------------------------------------------------------*/
00525 ret  ::asn::asnIA5String (type string) {
00526     # the IA5 string tag is 0x16
00527     # check for extended charachers
00528     if {[string length $string]!=[string bytelength $string]} {
00529     return -code error "Illegal character in IA5String"
00530     }
00531     set ascii [encoding convertto ascii $string]
00532     return [asnEncodeString 16 $ascii]
00533 }
00534 
00535 /* -----------------------------------------------------------------------------*/
00536 /*  asnNumericString : Encode a Numeric String type*/
00537 /* -----------------------------------------------------------------------------*/
00538 namespace asn {
00539     variable nonNumericChars {[^0-9 ]}
00540 }
00541 ret  ::asn::asnNumericString (type string) {
00542     # the Numeric String type has tag 0x12
00543     variable nonNumericChars
00544     if {[regexp $nonNumericChars $string]} {
00545         return -code error "Illegal character in Numeric String."
00546     }
00547     
00548     return [asnEncodeString 12 $string]
00549 }
00550 /* ----------------------------------------------------------------------*/
00551 /*  asnBMPString: Encode a Tcl string as Basic Multinligval (UCS2) string*/
00552 /* -----------------------------------------------------------------------*/
00553 ret  asn::asnBMPString  (type string) {
00554     if {$::tcl_platform(byteOrder) eq "littleEndian"} {
00555     set bytes ""
00556     foreach {lo hi} [split [encoding convertto unicode $string] ""] {
00557         append bytes $hi $lo
00558     }   
00559     } else {
00560     set bytes [encoding convertto unicode $string]
00561     }
00562     return [asnEncodeString 1e $bytes]
00563 }   
00564 /* ---------------------------------------------------------------------------*/
00565 /*  asnUTF8String: encode tcl string as UTF8 String*/
00566 /* ----------------------------------------------------------------------------*/
00567 ret  asn::asnUTF8String (type string) {
00568     return [asnEncodeString 0c [encoding convertto utf-8 $string]]
00569 }
00570 /* -----------------------------------------------------------------------------*/
00571 /*  asnEncodeString : Encode an RestrictedCharacter String*/
00572 /* -----------------------------------------------------------------------------*/
00573 ret  ::asn::asnEncodeString (type tag , type string) {
00574     set len [string length $string]
00575     return [binary format H2a*a$len $tag [asnLength $len] $string]    
00576 }
00577 
00578 /* -----------------------------------------------------------------------------*/
00579 /*  asnObjectIdentifier : Encode an Object Identifier value*/
00580 /* -----------------------------------------------------------------------------*/
00581 ret  ::asn::asnObjectIdentifier (type oid) {
00582     # the object identifier tag is 0x06
00583     
00584     if {[llength $oid] < 2} {
00585         return -code error "OID must have at least two subidentifiers."
00586     }
00587     
00588     # basic check that it is valid
00589     foreach identifier $oid {
00590         if {$identifier < 0} {
00591             return -code error \
00592         "Malformed OID. Identifiers must be positive Integers."
00593         }
00594     }
00595     
00596     if {[lindex $oid 0] > 2} {
00597             return -code error "First subidentifier must be 0,1 or 2"
00598     }
00599     if {[lindex $oid 1] > 39} {
00600             return -code error \
00601         "Second subidentifier must be between 0 and 39"
00602     }
00603     
00604     # handle the special cases directly
00605     switch [llength $oid] {
00606         2  {  return [binary format H2H2c 06 01 \
00607         [expr {[lindex $oid 0]*40+[lindex $oid 1]}]] }
00608         default {
00609               # This can probably be written much shorter. 
00610               # Just a first try that works...
00611               #
00612               set octets [binary format c \
00613         [expr {[lindex $oid 0]*40+[lindex $oid 1]}]]
00614               foreach identifier [lrange $oid 2 end] {
00615                   set d 128
00616                   if {$identifier < 128} {
00617                     set subidentifier [list $identifier]
00618                   } else {  
00619                     set subidentifier [list]
00620                     # find the largest divisor
00621                     
00622                     while {($identifier / $d) >= 128} { 
00623             set d [expr {$d * 128}] 
00624             }
00625                     # and construct the subidentifiers
00626                     set remainder $identifier
00627                     while {$d >= 128} {
00628                         set coefficient [expr {($remainder / $d) | 0x80}]
00629                         set remainder [expr {$remainder % $d}]
00630                         set d [expr {$d / 128}]
00631                         lappend subidentifier $coefficient
00632                     }
00633                     lappend subidentifier $remainder
00634                   }
00635                   append octets [binary format c* $subidentifier]
00636               }
00637               return [binary format H2a*a* 06 \
00638               [asnLength [string length $octets]] $octets]
00639         }
00640     }
00641 
00642 }
00643 
00644 /* -----------------------------------------------------------------------------*/
00645 /*  asnGetResponse : Read a ASN response from a channel.*/
00646 /* -----------------------------------------------------------------------------*/
00647 
00648 ret  ::asn::asnGetResponse (type sock , type data_, type var) {
00649     upvar 1 $data_var data
00650 
00651     # We expect a sequence here (tag 0x30). The code below is an
00652     # inlined replica of 'asnGetSequence', modified for reading from a
00653     # channel instead of a string.
00654 
00655     set tag [read $sock 1]
00656 
00657     if {$tag == "\x30"} {
00658     # The following code is a replica of 'asnGetLength', modified
00659     # for reading the bytes from the channel instead of a string.
00660 
00661         set len1 [read $sock 1]
00662         binary scan $len1 c num
00663         set length [expr {($num + 0x100) % 0x100}]
00664 
00665         if {$length  >= 0x080} {
00666         # The byte the read is not the length, but a prefix, and
00667         # the lower nibble tells us how many bytes follow.
00668 
00669             set len_length  [expr {$length & 0x7f}]
00670 
00671         # BUG: We should not perform the value extraction for an
00672         # BUG: improper length. It wastes cycles, and here it can
00673         # BUG: cause us trouble, reading more data than there is
00674         # BUG: on the channel. Depending on the channel
00675         # BUG: configuration an attacker can induce us to block,
00676         # BUG: causing a denial of service.
00677             set lengthBytes [read $sock $len_length]
00678 
00679             switch $len_length {
00680                 1 {
00681             binary scan $lengthBytes     c length 
00682             set length [expr {($length + 0x100) % 0x100}]
00683                 }
00684                 2 { binary scan $lengthBytes     S length }
00685                 3 { binary scan \x00$lengthBytes I length }
00686                 4 { binary scan $lengthBytes     I length }
00687                 default {
00688                     return -code error \
00689             "length information too long ($len_length)"
00690                 }
00691             }
00692         }
00693 
00694     # Now that the length is known we get the remainder,
00695     # i.e. payload, and construct proper in-memory BER encoded
00696     # sequence.
00697 
00698         set rest [read $sock $length]
00699         set data [binary format aa*a$length $tag [asnLength $length] $rest]
00700     }  else {
00701     # Generate an error message if the data is not a sequence as
00702     # we expected.
00703 
00704         set tag_hex ""
00705         binary scan $tag H2 tag_hex
00706         return -code error "unknown start tag [string length $tag] $tag_hex"
00707     }
00708 }
00709 
00710 /* -----------------------------------------------------------------------------*/
00711 /*  asnGetByte : Retrieve a single byte from the data (unsigned)*/
00712 /* -----------------------------------------------------------------------------*/
00713 
00714 ret  ::asn::asnGetByte (type data_, type var , type byte_, type var) {
00715     upvar $data_var data $byte_var byte
00716     
00717     binary scan [string index $data 0] c byte
00718     set byte [expr {($byte + 0x100) % 0x100}]  
00719     set data [string range $data 1 end]
00720 
00721     return
00722 }
00723 
00724 /* -----------------------------------------------------------------------------*/
00725 /*  asnPeekByte : Retrieve a single byte from the data (unsigned) */
00726 /*                without removing it.*/
00727 /* -----------------------------------------------------------------------------*/
00728 
00729 ret  ::asn::asnPeekByte (type data_, type var , type byte_, type var , optional offset =0) {
00730     upvar 1 $data_var data $byte_var byte
00731     
00732     binary scan [string index $data $offset] c byte
00733     set byte [expr {($byte + 0x100) % 0x100}]  
00734 
00735     return
00736 }
00737 
00738 /* -----------------------------------------------------------------------------*/
00739 /*  asnRetag: Remove an explicit tag with the real newTag*/
00740 /* */
00741 /* -----------------------------------------------------------------------------*/
00742 ret  ::asn::asnRetag (type data_, type var , type newTag) {
00743     upvar 1 $data_var data 
00744     set tag ""
00745     set type ""
00746     set len [asnPeekTag data tag type dummy]    
00747     asnGetBytes data $len tagbytes
00748     set data [binary format c* $newTag]$data
00749 }
00750 
00751 /* -----------------------------------------------------------------------------*/
00752 /*  asnGetBytes : Retrieve a block of 'length' bytes from the data.*/
00753 /* -----------------------------------------------------------------------------*/
00754 
00755 ret  ::asn::asnGetBytes (type data_, type var , type length , type bytes_, type var) {
00756     upvar 1 $data_var data  $bytes_var bytes
00757 
00758     incr length -1
00759     set bytes [string range $data 0 $length]
00760     incr length
00761     set data [string range $data $length end]
00762 
00763     return
00764 }
00765 
00766 /* -----------------------------------------------------------------------------*/
00767 /*  asnPeekTag : Decode the tag value*/
00768 /* -----------------------------------------------------------------------------*/
00769 
00770 ret  ::asn::asnPeekTag (type data_, type var , type tag_, type var , type tagtype_, type var , type constr_, type var) {
00771     upvar 1 $data_var data $tag_var tag $tagtype_var tagtype $constr_var constr
00772     
00773     set type 0  
00774     set offset 0
00775     asnPeekByte data type $offset
00776     # check if we have a simple tag, < 31, which fits in one byte
00777      
00778     set tval [expr {$type & 0x1f}]
00779     if {$tval == 0x1f} {
00780     # long tag, max 64-bit with Tcl 8.4, unlimited with 8.5 bignum
00781     asnPeekByte data tagbyte [incr offset]
00782     set tval [expr {wide($tagbyte & 0x7f)}]
00783     while {($tagbyte & 0x80)} {
00784         asnPeekByte data tagbyte [incr offset] 
00785         set tval [expr {($tval << 7) + ($tagbyte & 0x7f)}]
00786     }
00787     } 
00788 
00789     set tagtype [lindex {UNIVERSAL APPLICATION CONTEXT PRIVATE} \
00790     [expr {($type & 0xc0) >>6}]]
00791     set tag $tval
00792     set constr [expr {($type & 0x20) > 0}]
00793 
00794     return [incr offset]    
00795 }
00796 
00797 /* -----------------------------------------------------------------------------*/
00798 /*  asnTag : Build a tag value*/
00799 /* -----------------------------------------------------------------------------*/
00800 
00801 ret  ::asn::asnTag (type tagnumber , optional class =UNIVERSAL , optional tagstyle =P) {
00802     set first 0
00803     if {$tagnumber < 31} {
00804     # encode everything in one byte
00805     set first $tagnumber    
00806     set bytes [list]
00807     } else {
00808     # multi-byte tag
00809     set first 31
00810     set bytes [list [expr {$tagnumber & 0x7f}]]
00811     set tagnumber [expr {$tagnumber >> 7}]
00812     while {$tagnumber > 0} {
00813         lappend bytes [expr {($tagnumber & 0x7f)+0x80}]
00814         set tagnumber [expr {$tagnumber >>7}]   
00815     }
00816 
00817     }
00818     
00819     if {$tagstyle eq "C" || $tagstyle == 1 } {incr first 32}
00820     switch -glob -- $class {
00821     U* {            ;# UNIVERSAL } 
00822     A* { incr first 64  ;# APPLICATION }
00823     C* { incr first 128 ;# CONTEXT }
00824     P* { incr first 192 ;# PRIVATE }
00825     default {
00826         return -code error "Unknown tag class \"$class\""
00827     }   
00828     }
00829     if {[llength $bytes] > 0} {
00830     # long tag
00831     set rbytes [list]
00832     for {set i [expr {[llength $bytes]-1}]} {$i >= 0} {incr i -1} {
00833         lappend rbytes [lindex $bytes $i]
00834     }
00835     return [binary format cc* $first $rbytes ]
00836     } 
00837     return [binary format c $first]
00838 }
00839 
00840 /* -----------------------------------------------------------------------------*/
00841 /*  asnGetLength : Decode an ASN length value (See notes)*/
00842 /* -----------------------------------------------------------------------------*/
00843 
00844 ret  ::asn::asnGetLength (type data_, type var , type length_, type var) {
00845     upvar 1 $data_var data  $length_var length
00846 
00847     asnGetByte data length
00848     if {$length == 0x080} {
00849         return -code error "Indefinite length BER encoding not yet supported"
00850     }
00851     if {$length > 0x080} {
00852     # The retrieved byte is a prefix value, and the integer in the
00853     # lower nibble tells us how many bytes were used to encode the
00854     # length data following immediately after this prefix.
00855 
00856         set len_length [expr {$length & 0x7f}]
00857         
00858         if {[string length $data] < $len_length} {
00859             return -code error \
00860         "length information invalid, not enough octets left" 
00861         }
00862         
00863         asnGetBytes data $len_length lengthBytes
00864 
00865         switch $len_length {
00866             1 {
00867         # Efficiently coded data will not go through this
00868         # path, as small length values can be coded directly,
00869         # without a prefix.
00870 
00871             binary scan $lengthBytes     c length 
00872             set length [expr {($length + 0x100) % 0x100}]
00873             }
00874             2 { binary scan $lengthBytes     S length 
00875             set length [expr {($length + 0x10000) % 0x10000}]
00876             }
00877             3 { binary scan \x00$lengthBytes I length 
00878             set length [expr {($length + 0x1000000) % 0x1000000}]
00879             }
00880             4 { binary scan $lengthBytes     I length 
00881             set length [expr {(wide($length) + 0x100000000) % 0x100000000}]
00882             }
00883             default {                
00884                 binary scan $lengthBytes H* hexstr
00885                 # skip leading zeros which are allowed by BER
00886                 set hexlen [string trimleft $hexstr 0] 
00887                 # check if it fits into a 64-bit signed integer
00888                 if {[string length $hexlen] > 16} {
00889                     return -code error -errorcode {ARITH IOVERFLOW 
00890                             {Length value too large for normal use, try asnGetBigLength}} \
00891                 "Length value to large"
00892                 } elseif {  [string length $hexlen] == 16 \
00893             && ([string index $hexlen 0] & 0x8)} { 
00894                     # check most significant bit, if set we need bignum
00895                     return -code error -errorcode {ARITH IOVERFLOW 
00896                             {Length value too large for normal use, try asnGetBigLength}} \
00897                 "Length value to large"
00898                 } else {
00899                     scan $hexstr "%lx" length
00900                 }
00901             }
00902         }
00903     }
00904     return
00905 }
00906 
00907 
00908 /* -----------------------------------------------------------------------------*/
00909 /*  asnGetBigLength : Retrieve a length that can not be represented in 63-bit*/
00910 /* -----------------------------------------------------------------------------*/
00911 
00912 ret  ::asn::asnGetBigLength (type data_, type var , type biglength_, type var) {
00913 
00914     # Does any real world code really need this? 
00915     # If we encounter this, we are doomed to fail anyway, 
00916     # (there would be an Exabyte inside the data_var, )
00917     #
00918     # So i implement it just for completness.
00919     # 
00920     package require math::bignum
00921     
00922     upvar 1 $data_var data  $biglength_var length
00923 
00924     asnGetByte data length
00925     if {$length == 0x080} {
00926         return -code error "Indefinite length BER encoding not yet supported"
00927     }
00928     if {$length > 0x080} {
00929     # The retrieved byte is a prefix value, and the integer in the
00930     # lower nibble tells us how many bytes were used to encode the
00931     # length data following immediately after this prefix.
00932 
00933         set len_length [expr {$length & 0x7f}]
00934         
00935         if {[string length $data] < $len_length} {
00936             return -code error \
00937         "length information invalid, not enough octets left" 
00938         }
00939         
00940         asnGetBytes data $len_length lengthBytes
00941         binary scan $lengthBytes H* hexlen
00942         set length [math::bignum::fromstr $hexlen 16]
00943     }
00944     return
00945 }
00946 
00947 /* -----------------------------------------------------------------------------*/
00948 /*  asnGetInteger : Retrieve integer.*/
00949 /* -----------------------------------------------------------------------------*/
00950 
00951 ret  ::asn::asnGetInteger (type data_, type var , type int_, type var) {
00952     # Tag is 0x02. 
00953 
00954     upvar 1 $data_var data $int_var int
00955 
00956     asnGetByte   data tag
00957 
00958     if {$tag != 0x02} {
00959         return -code error \
00960             [format "Expected Integer (0x02), but got %02x" $tag]
00961     }
00962 
00963     asnGetLength data len
00964     asnGetBytes  data $len integerBytes
00965 
00966     set int ?
00967 
00968     switch $len {
00969         1 { binary scan $integerBytes     c int }
00970         2 { binary scan $integerBytes     S int }
00971         3 { 
00972             # check for negative int and pad 
00973             scan [string index $integerBytes 0] %c byte
00974             if {$byte & 128} {
00975                 binary scan \xff$integerBytes I int
00976             } else {
00977                 binary scan \x00$integerBytes I int 
00978             }
00979           }
00980         4 { binary scan $integerBytes     I int }
00981         5 -
00982         6 -
00983         7 -
00984         8 {
00985             # check for negative int and pad
00986             scan [string index $integerBytes 0] %c byte
00987             if {$byte & 128} {
00988                 set pad [string repeat \xff [expr {8-$len}]]
00989             } else {
00990                 set pad [string repeat \x00 [expr {8-$len}]]
00991             }
00992             binary scan $pad$integerBytes W int 
00993         }
00994         default {
00995         # Too long, or prefix coding was used.
00996             return -code error "length information too long"
00997         }
00998     }
00999     return
01000 }
01001 
01002 /* -----------------------------------------------------------------------------*/
01003 /*  asnGetBigInteger : Retrieve a big integer.*/
01004 /* -----------------------------------------------------------------------------*/
01005 
01006 ret  ::asn::asnGetBigInteger (type data_, type var , type bignum_, type var) {
01007     # require math::bignum only if it is used
01008     package require math::bignum
01009 
01010     # Tag is 0x02. We expect that the length of the integer is coded with
01011     # maximal efficiency, i.e. without a prefix 0x81 prefix. If a prefix
01012     # is used this decoder will fail.
01013 
01014     upvar 1 $data_var data $bignum_var bignum
01015 
01016     asnGetByte   data tag
01017 
01018     if {$tag != 0x02} {
01019         return -code error \
01020             [format "Expected Integer (0x02), but got %02x" $tag]
01021     }
01022 
01023     asnGetLength data len
01024     asnGetBytes  data $len integerBytes
01025     
01026     binary scan $integerBytes H* hex
01027     set bignum [math::bignum::fromstr $hex 16]
01028     set bits [math::bignum::bits $bignum]
01029     set exp [math::bignum::pow \
01030         [math::bignum::fromstr 2] \
01031         [math::bignum::fromstr $bits]]
01032     set big [math::bignum::sub $bignum $exp]
01033     set bignum $big
01034     
01035     return    
01036 }
01037 
01038 
01039 
01040 /* -----------------------------------------------------------------------------*/
01041 /*  asnGetEnumeration : Retrieve an enumeration id*/
01042 /* -----------------------------------------------------------------------------*/
01043 
01044 ret  ::asn::asnGetEnumeration (type data_, type var , type enum_, type var) {
01045     # This is like 'asnGetInteger', except for a different tag.
01046 
01047     upvar 1 $data_var data $enum_var enum
01048 
01049     asnGetByte   data tag
01050 
01051     if {$tag != 0x0a} {
01052         return -code error \
01053             [format "Expected Enumeration (0x0a), but got %02x" $tag]
01054     }
01055 
01056     asnGetLength data len
01057     asnGetBytes  data $len integerBytes
01058     set enum ?
01059 
01060     switch $len {
01061         1 { binary scan $integerBytes     c enum }
01062         2 { binary scan $integerBytes     S enum }
01063         3 { binary scan \x00$integerBytes I enum }
01064         4 { binary scan $integerBytes     I enum }
01065         default {
01066             return -code error "length information too long"
01067         }
01068     }
01069     return
01070 }
01071 
01072 /* -----------------------------------------------------------------------------*/
01073 /*  asnGetOctetString : Retrieve arbitrary string.*/
01074 /* -----------------------------------------------------------------------------*/
01075 
01076 ret  ::asn::asnGetOctetString (type data_, type var , type string_, type var) {
01077     # Here we need the full decoder for length data.
01078 
01079     upvar 1 $data_var data $string_var string
01080     
01081     asnGetByte data tag
01082     if {$tag != 0x04} { 
01083         return -code error \
01084             [format "Expected Octet String (0x04), but got %02x" $tag]
01085     }
01086     asnGetLength data length
01087     asnGetBytes  data $length temp
01088     set string $temp
01089     return
01090 }
01091 
01092 /* -----------------------------------------------------------------------------*/
01093 /*  asnGetSequence : Retrieve Sequence data for further decoding.*/
01094 /* -----------------------------------------------------------------------------*/
01095 
01096 ret  ::asn::asnGetSequence (type data_, type var , type sequence_, type var) {
01097     # Here we need the full decoder for length data.
01098 
01099     upvar 1 $data_var data $sequence_var sequence
01100 
01101     asnGetByte data tag
01102     if {$tag != 0x030} { 
01103         return -code error \
01104             [format "Expected Sequence (0x30), but got %02x" $tag]
01105     }    
01106     asnGetLength data length
01107     asnGetBytes  data $length temp
01108     set sequence $temp
01109     return
01110 }
01111 
01112 /* -----------------------------------------------------------------------------*/
01113 /*  asnGetSet : Retrieve Set data for further decoding.*/
01114 /* -----------------------------------------------------------------------------*/
01115 
01116 ret  ::asn::asnGetSet (type data_, type var , type set_, type var) {
01117     # Here we need the full decoder for length data.
01118 
01119     upvar 1 $data_var data $set_var set
01120 
01121     asnGetByte data tag
01122     if {$tag != 0x031} { 
01123         return -code error \
01124             [format "Expected Set (0x31), but got %02x" $tag]
01125     }    
01126     asnGetLength data length
01127     asnGetBytes  data $length temp
01128     set set $temp
01129     return
01130 }
01131 
01132 /* -----------------------------------------------------------------------------*/
01133 /*  asnGetApplication*/
01134 /* -----------------------------------------------------------------------------*/
01135 
01136 ret  ::asn::asnGetApplication (type data_, type var , type appNumber_, type var , optional content_var ={) {encodingType_var {}} } {
01137     upvar 1 $data_var data $appNumber_var appNumber
01138 
01139     asnGetByte   data tag
01140     asnGetLength data length
01141 
01142     if {($tag & 0xC0) != 0x40} {
01143         return -code error \
01144             [format "Expected Application, but got %02x" $tag]
01145     }    
01146     if {$encodingType_var != {}} {
01147     upvar 1 $encodingType_var encodingType
01148      encodingType =  [expr {($tag & 0x20) > 0}]
01149     }
01150      appNumber =  [expr {$tag & 0x1F}]
01151     if {[string length $content_var]} {
01152         upvar 1 $content_var content
01153         asnGetBytes data $length content
01154     }   
01155     return
01156 }
01157 
01158 /* -----------------------------------------------------------------------------*/
01159 /*  asnGetBoolean: decode a boolean value*/
01160 /* -----------------------------------------------------------------------------*/
01161 
01162 ret  asn::asnGetBoolean (type data_, type var , type bool_, type var) {
01163     upvar 1 $data_var data $bool_var bool
01164 
01165     asnGetByte data tag
01166     if {$tag != 0x01} {
01167         return -code error \
01168             [format "Expected Boolean (0x01), but got %02x" $tag]
01169     }
01170 
01171     asnGetLength data length
01172     asnGetByte data byte
01173     set bool [expr {$byte == 0 ? 0 : 1}]    
01174     return
01175 }
01176 
01177 /* -----------------------------------------------------------------------------*/
01178 /*  asnGetUTCTime: Extract an UTC Time string from the data. Returns a string*/
01179 /*                 representing an UTC Time.*/
01180 /* */
01181 /* -----------------------------------------------------------------------------*/
01182 
01183 ret  asn::asnGetUTCTime (type data_, type var , type utc_, type var) {
01184     upvar 1 $data_var data $utc_var utc
01185 
01186     asnGetByte data tag
01187     if {$tag != 0x17} {
01188         return -code error \
01189             [format "Expected UTCTime (0x17), but got %02x" $tag]
01190     }
01191 
01192     asnGetLength data length
01193     asnGetBytes data $length bytes
01194     
01195     # this should be ascii, make it explicit
01196     set bytes [encoding convertfrom ascii $bytes]
01197     binary scan $bytes a* utc
01198     
01199     return
01200 }
01201 
01202 
01203 /* -----------------------------------------------------------------------------*/
01204 /*  asnGetBitString: Extract a Bit String value (a string of 0/1s) from the*/
01205 /*                   ASN.1 data.*/
01206 /* */
01207 /* -----------------------------------------------------------------------------*/
01208 
01209 ret  asn::asnGetBitString (type data_, type var , type bitstring_, type var) {
01210     upvar 1 $data_var data $bitstring_var bitstring
01211 
01212     asnGetByte data tag
01213     if {$tag != 0x03} {
01214         return -code error \
01215             [format "Expected Bit String (0x03), but got %02x" $tag]
01216     }
01217     
01218     asnGetLength data length
01219     # get the number of padding bits used at the end
01220     asnGetByte data padding
01221     incr length -1
01222     asnGetBytes data $length bytes
01223     binary scan $bytes B* bits
01224     
01225     # cut off the padding bits
01226     set bits [string range $bits 0 end-$padding]
01227     set bitstring $bits
01228 }
01229 
01230 /* -----------------------------------------------------------------------------*/
01231 /*  asnGetObjectIdentifier: Decode an ASN.1 Object Identifier (OID) into*/
01232 /*                          a Tcl list of integers.*/
01233 /* -----------------------------------------------------------------------------*/
01234 
01235 ret  asn::asnGetObjectIdentifier (type data_, type var , type oid_, type var) {
01236       upvar 1 $data_var data $oid_var oid
01237 
01238       asnGetByte data tag
01239       if {$tag != 0x06} {
01240         return -code error \
01241             [format "Expected Object Identifier (0x06), but got %02x" $tag]  
01242       }
01243       asnGetLength data length
01244       
01245       # the first byte encodes the OID parts in position 0 and 1
01246       asnGetByte data val
01247       set oid [expr {$val / 40}]
01248       lappend oid [expr {$val % 40}]
01249       incr length -1
01250       
01251       # the next bytes encode the remaining parts of the OID
01252       set bytes [list]
01253       set incomplete 0
01254       while {$length} {
01255         asnGetByte data octet
01256         incr length -1
01257         if {$octet < 128} {
01258             set oidval $octet
01259             set mult 128
01260             foreach byte $bytes {
01261                 if {$byte != {}} {
01262                 incr oidval [expr {$mult*$byte}]    
01263                 set mult [expr {$mult*128}]
01264                 }
01265             }
01266             lappend oid $oidval
01267             set bytes [list]
01268             set incomplete 0
01269         } else {
01270             set byte [expr {$octet-128}]
01271             set bytes [concat [list $byte] $bytes]
01272             set incomplete 1
01273         }                      
01274       }
01275       if {$incomplete} {
01276         return -code error "OID Data is incomplete, not enough octets."
01277       }
01278       return
01279 }
01280 
01281 /* -----------------------------------------------------------------------------*/
01282 /*  asnGetContext: Decode an explicit context tag */
01283 /* */
01284 /* -----------------------------------------------------------------------------*/
01285 
01286 ret  ::asn::asnGetContext (type data_, type var , type contextNumber_, type var , optional content_var ={) {encodingType_var {}}} {
01287     upvar 1 $data_var data $contextNumber_var contextNumber 
01288     
01289     asnGetByte   data tag
01290     asnGetLength data length
01291 
01292     if {($tag & 0xC0) != 0x80} {
01293         return -code error \
01294             [format "Expected Context, but got %02x" $tag]
01295     }    
01296     if {$encodingType_var != {}} { 
01297     upvar 1 $encodingType_var encodingType 
01298      encodingType =  [expr {($tag & 0x20) > 0}]
01299     }
01300      contextNumber =  [expr {$tag & 0x1F}]
01301     if {[string length $content_var]} {
01302         upvar 1 $content_var content
01303         asnGetBytes data $length content
01304     }   
01305     return
01306 }
01307 
01308 
01309 /* -----------------------------------------------------------------------------*/
01310 /*  asnGetNumericString: Decode a Numeric String from the data*/
01311 /* -----------------------------------------------------------------------------*/
01312 
01313 ret  ::asn::asnGetNumericString (type data_, type var , type print_, type var) {
01314     upvar 1 $data_var data $print_var print
01315 
01316     asnGetByte data tag
01317     if {$tag != 0x12} {
01318         return -code error \
01319             [format "Expected Numeric String (0x12), but got %02x" $tag]  
01320     }
01321     asnGetLength data length 
01322     asnGetBytes data $length string
01323     set print [encoding convertfrom ascii $string]
01324     return
01325 }
01326 
01327 /* -----------------------------------------------------------------------------*/
01328 /*  asnGetPrintableString: Decode a Printable String from the data*/
01329 /* -----------------------------------------------------------------------------*/
01330 
01331 ret  ::asn::asnGetPrintableString (type data_, type var , type print_, type var) {
01332     upvar 1 $data_var data $print_var print
01333 
01334     asnGetByte data tag
01335     if {$tag != 0x13} {
01336         return -code error \
01337             [format "Expected Printable String (0x13), but got %02x" $tag]  
01338     }
01339     asnGetLength data length 
01340     asnGetBytes data $length string
01341     set print [encoding convertfrom ascii $string]
01342     return
01343 }
01344 
01345 /* -----------------------------------------------------------------------------*/
01346 /*  asnGetIA5String: Decode a IA5(ASCII) String from the data*/
01347 /* -----------------------------------------------------------------------------*/
01348 
01349 ret  ::asn::asnGetIA5String (type data_, type var , type print_, type var) {
01350     upvar 1 $data_var data $print_var print
01351 
01352     asnGetByte data tag
01353     if {$tag != 0x16} {
01354         return -code error \
01355             [format "Expected IA5 String (0x16), but got %02x" $tag]  
01356     }
01357     asnGetLength data length 
01358     asnGetBytes data $length string
01359     set print [encoding convertfrom ascii $string]
01360     return
01361 }
01362 /* ------------------------------------------------------------------------*/
01363 /*  asnGetBMPString: Decode Basic Multiningval (UCS2 string) from data*/
01364 /* ------------------------------------------------------------------------*/
01365 ret  asn::asnGetBMPString (type data_, type var , type print_, type var) {
01366     upvar 1 $data_var data $print_var print
01367     asnGetByte data tag
01368     if {$tag != 0x1e} {
01369         return -code error \
01370             [format "Expected BMP String (0x1e), but got %02x" $tag]  
01371     }
01372     asnGetLength data length 
01373     asnGetBytes data $length string
01374     if {$::tcl_platform(byteOrder) eq "littleEndian"} {
01375         set str2 ""
01376         foreach {hi lo} [split $string ""] {
01377             append str2 $lo $hi
01378         }
01379     } else {
01380         set str2 $string
01381     }
01382     set print [encoding convertfrom unicode $str2]
01383     return
01384 }   
01385 /* ------------------------------------------------------------------------*/
01386 /*  asnGetUTF8String: Decode UTF8 string from data*/
01387 /* ------------------------------------------------------------------------*/
01388 ret  asn::asnGetUTF8String (type data_, type var , type print_, type var) {
01389     upvar 1 $data_var data $print_var print
01390     asnGetByte data tag
01391     if {$tag != 0x0c} {
01392         return -code error \
01393             [format "Expected UTF8 String (0x0c), but got %02x" $tag]  
01394     }
01395     asnGetLength data length 
01396     asnGetBytes data $length string
01397     #there should be some error checking to see if input is
01398     #properly-formatted utf8
01399     set print [encoding convertfrom utf-8 $string]
01400     
01401     return
01402 }   
01403 /* -----------------------------------------------------------------------------*/
01404 /*  asnGetNull: decode a NULL value*/
01405 /* -----------------------------------------------------------------------------*/
01406 
01407 ret  ::asn::asnGetNull (type data_, type var) {
01408     upvar 1 $data_var data 
01409 
01410     asnGetByte data tag
01411     if {$tag != 0x05} {
01412         return -code error \
01413             [format "Expected NULL (0x05), but got %02x" $tag]
01414     }
01415 
01416     asnGetLength data length
01417     asnGetBytes data $length bytes
01418     
01419     # we do not check the null data, all bytes must be 0x00
01420     
01421     return
01422 }
01423 
01424 /* ----------------------------------------------------------------------------*/
01425 /*  MultiType string routines*/
01426 /* ----------------------------------------------------------------------------*/
01427 
01428 namespace asn {
01429     variable stringTypes
01430     array  stringTypes =  {
01431         12 NumericString 
01432         13 PrintableString 
01433         16 IA5String 
01434         1e BMPString 
01435         0c UTF8String 
01436         14 T61String
01437         15 VideotexString
01438         1a VisibleString
01439         1b GeneralString
01440         1c UniversalString
01441     }   
01442     variable defaultStringType UTF8
01443 }   
01444 /* ---------------------------------------------------------------------------*/
01445 /*  asnGetString - get readable string automatically detecting its type*/
01446 /* ---------------------------------------------------------------------------*/
01447 ret  ::asn::asnGetString (type data_, type var , type print_, type var , optional type_var ={)} {
01448     variable stringTypes
01449     upvar 1 $data_var data $print_var print
01450     asnPeekByte data tag
01451     set tag [format %02x $tag]
01452     if {![info exists stringTypes($tag)]} {
01453         return -code error "Expected one of string types, but got $tag"
01454     }
01455     asnGet$stringTypes($tag) data print
01456     if {[string length $type_var]} {
01457         upvar $type_var type
01458          type =  $stringTypes($tag)
01459     }   
01460 }
01461 /* ---------------------------------------------------------------------*/
01462 /*  defaultStringType - set or query default type for unrestricted strings*/
01463 /* ---------------------------------------------------------------------*/
01464 ret  ::asn::defaultStringType (optional type ={)} {
01465     variable defaultStringType
01466     if {![string length $type]} {
01467         return $defaultStringType
01468     }
01469     if {$type ne "BMP" && $type ne "UTF8"} {
01470         return -code error "Invalid default string type. Should be one of BMP, UTF8"
01471     }
01472      defaultStringType =  $type
01473     return
01474 }   
01475 
01476 /* ---------------------------------------------------------------------------*/
01477 /*  asnString - encode readable string into most restricted type possible*/
01478 /* ---------------------------------------------------------------------------*/
01479 
01480 ret  ::asn::asnString (type string) {
01481     variable nonPrintableChars
01482     variable nonNumericChars
01483     if {[string length $string]!=[string bytelength $string]} {
01484     # There are non-ascii character
01485         variable defaultStringType
01486         return [asn${defaultStringType}String $string]
01487     } elseif {![regexp $nonNumericChars $string]} {
01488         return [asnNumericString $string]
01489     } elseif {![regexp $nonPrintableChars $string]} {
01490         return [asnPrintableString $string]
01491     } else {
01492         return [asnIA5String $string]
01493     }   
01494 }
01495 
01496 /* -----------------------------------------------------------------------------*/
01497 package provide asn 0.8.1
01498 
01499 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1