├── .github └── workflows │ ├── linux.yml │ └── matrix.yml ├── .gitignore ├── ChangeLog ├── Makefile.PL ├── OldChanges ├── README.md ├── cpanfile ├── dist.ini ├── examples ├── ldap ├── ldap-search ├── tsa3161 └── x509decode ├── lib └── Convert │ ├── ASN1.pm │ ├── ASN1.pod │ └── ASN1 │ ├── Debug.pm │ ├── IO.pm │ ├── _decode.pm │ ├── _encode.pm │ └── parser.pm ├── mkparse ├── parser.y └── t ├── 00prim.t ├── 01tag.t ├── 02seq.t ├── 03seqof.t ├── 04opt.t ├── 05time.t ├── 06bigint.t ├── 07input.dat ├── 07io.t ├── 08set.t ├── 09contr.t ├── 10choice.t ├── 11explicit.t ├── 11indef.t ├── 12der.t ├── 13utf8.t ├── 14any.t ├── 15extseq.t ├── 16extset.t ├── 17extchoice.t ├── 18tagdefault.t ├── 19issue14.t ├── 99misc.t ├── aj.cer ├── aj2.cer ├── allianz_root.cer ├── dsacert.der ├── funcs.pl ├── new_root_ca.cer ├── pgpextension.der ├── subca_2.cer ├── telesec_799972029.crt ├── verisign.der └── x509.t /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: 4 | - push 5 | 6 | jobs: 7 | perl: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | perl-version: 12 | - '5.8' 13 | - '5.10' 14 | - '5.12' 15 | - '5.14' 16 | - '5.16' 17 | - '5.18' 18 | - '5.20' 19 | - '5.22' 20 | - '5.24' 21 | - '5.26' 22 | - '5.28' 23 | - '5.30' 24 | - '5.32' 25 | container: 26 | image: perl:${{ matrix.perl-version }} 27 | steps: 28 | - uses: actions/checkout@v1 29 | #- name: Install OS Packages 30 | # run: | 31 | # apt-get update; 32 | # apt-get -y install xmlsec1; 33 | - name: Install Dependencies 34 | run: | 35 | cpanm --installdeps . 36 | - name: Build Module 37 | run: | 38 | perl Makefile.PL; 39 | make 40 | - name: Run Tests 41 | run: | 42 | prove -lr -l -b -I inc t 43 | -------------------------------------------------------------------------------- /.github/workflows/matrix.yml: -------------------------------------------------------------------------------- 1 | name: Perl Matrix Testing 2 | on: 3 | - push 4 | jobs: 5 | build: 6 | runs-on: ${{ matrix.os }} 7 | strategy: 8 | matrix: 9 | os: [ 'windows-latest', 'macos-latest'] 10 | perl: [ '5.34', '5.32', '5.30' ] 11 | name: Perl ${{ matrix.perl }} on ${{ matrix.os }} 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up perl 15 | uses: shogo82148/actions-setup-perl@v1 16 | with: 17 | perl-version: ${{ matrix.perl }} 18 | - run: perl -V 19 | - run: cpanm --installdeps . 20 | - run: prove -lv t 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | blib 3 | pm_to_blib 4 | y.tab.pl 5 | _dev 6 | Convert-ASN1-* 7 | .build/ 8 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | {{$NEXT}} 2 | 3 | 0.34 -- Mon Aug 07 19:46:22 ADT 2023 4 | 5 | Bug Fixes 6 | 7 | Thanks to chrisridd and fschlich for these bug fixes 8 | * Fixes: #47 2nd - Debug output using print instead of printf 9 | * Fixes #47 Debug output using print instead of printf 10 | * ASN1.pod: fix minor typo 11 | 12 | 0.33 -- Wed Sep 22 19:49:53 ADT 2021 13 | 14 | * Official CPAN release of 0.33 15 | 16 | 0.32 -- Tue Sep 21 18:45:20 ADT 2021 17 | 18 | * examples/x509decode: parameters are optional for AlgorithmIdentifier when using [ec]dsa algorithms 19 | * t/19issue14.t: issue with warnings 20 | * Fixes #44 Redundant argument in printf warning 21 | * examples/x509decode: fix prototype error 22 | * examples/ldap: fix asn1_dump not working 23 | 24 | 0.31 -- Wed Jun 02 22:28:29 ADT 2021 25 | 26 | * Official CPAN release of 0.30 27 | 28 | 0.30 -- Sat May 29 21:56:29 ADT 2021 29 | 30 | * Fixes #43 - Perl 5.35.1 makes scalar assignment to undef a compile time error 31 | 32 | 0.29 -- Mon May 24 18:27:46 ADT 2021 33 | 34 | * Release Version no changes from 0.28 35 | * Increment Version in Makefile.PL 36 | 37 | 0.28 -- Sun May 23 18:03:47 ADT 2021 38 | 39 | * Add github actions for repo testing and add new maintainer [Timothy Legge] 40 | * typo fix [Adam Leskis] 41 | * Fix test failures under Perl 5.26+ without '.' in @INC [Kent Fredric (KENTNL)] 42 | * Correct a typo - SYNOPSIS [Shlomi Fish] 43 | * unsafe decoding CVE-2013-7488 [Dana Jacobsen] 44 | * Typo fixes from dsteinbrunner [David Steinbrunner] 45 | * Add files via upload [Peter Sylvester] 46 | 47 | 0.27 -- Wed Jun 25 13:47:28 CDT 2014 48 | * Avoid recursion when encountering an invalid tag and length 49 | * Avoid negative repetition counts (5.22 compatibility) [Alex Vandiver] 50 | * Use pack/unpack instead of chr/org to avoid encoding issues 51 | 52 | 0.26 -- Sat Jun 09 13:30:16 CDT 2012 53 | * Avoid Modification of a read-only value attempted errors 54 | * dump Data::Dumper strings when rtest fails 55 | * Add tests from Crypt::X509 that found the last Modification of a read-only value attempted error 56 | 57 | 0.25 -- Fri Jun 08 19:31:59 CDT 2012 58 | * fix a spelling mistake [gregor herrmann] 59 | * Add use strict and fix errors 60 | * Fix decoding of indefinite sequences with ANY 61 | * POD additions for ANY 62 | * EXPLICIT ops must pass their cVAR to their child op 63 | * Swallow a , after a } 64 | 65 | 0.24 -- Mon Jun 04 17:11:08 CDT 2012 66 | * spelling fix [gregor herrmann] 67 | * Added tests for extension markers (fails currently) [Niels Laukens] 68 | * Bugfix for extension markerss in SETs and CHOICEs [Niels Laukens] 69 | * Skip attempting to encode opEXTENSIONS inside of CHOICE 70 | * Support for default explicit tagging [Peter Mogensen] 71 | * Added tests and doc for explicit tagging [Peter Mogensen] 72 | * Add new opEXPLICIT for sequences that are inserted for EXPLICIT tags 73 | 74 | 0.23 -- Thu May 03 16:32:46 CDT 2012 75 | * Added support for EXTENSION MARKERS (...) (Niels Laukens) 76 | * Switch to Dist::Zilla for building 77 | 78 | Convert::ASN1 0.21 -- Mon Sep 15 14:34:45 CDT 2008 79 | 80 | Bug Fixes 81 | * Fix decoding of OIDs with large value for 2nd element 82 | 83 | Enhancements 84 | * Support for SEQUENCE with no elements in ASN.1 85 | * Added ability to define what value is put in perl data structory when decoding NULL elements 86 | 87 | Convert::ASN1 0.21 -- Fri Feb 2 20:24:59 CST 2007 88 | 89 | Bug Fixes 90 | * Fixed problem with indefinite encoding inside an indefinite encoding 91 | 92 | Enhancements 93 | * Added support for type-local ANY DEFINED BY (patch from Leif Johansson) 94 | 95 | Convert::ASN1 0.20 -- Tue Feb 21 18:47:43 CST 2006 96 | 97 | Bug Fixes 98 | * Fixed issues with decoding when calling script contains use encoding 'utf8' 99 | * Allow zero-length timestamps to be extracted 100 | * Fix incorrect length encoding for bitstrings 101 | 102 | Enhancements 103 | * Support encoding bigint's as BCD 104 | 105 | 106 | Convert::ASN1 0.19 -- Mon Apr 18 19:40:32 CDT 2005 107 | 108 | Bug Fixes 109 | * Fixed reading of packets with indefinite-length encodings 110 | 111 | Enhancements 112 | * Add support for BCDString 113 | 114 | 115 | 116 | 117 | 2003-10-08 15:29 Graham Barr 118 | 119 | * lib/Convert/ASN1.pm: 120 | 121 | Release 0.18 122 | 123 | 2003-10-08 15:28 Graham Barr 124 | 125 | * MANIFEST, Makefile.PL, t/0-signature.t: 126 | 127 | Use Module::Build to build Makefile and add SIGNATURE 128 | 129 | 2003-10-08 13:28 Graham Barr 130 | 131 | * lib/Convert/: ASN1.pm, ASN1/_encode.pm: 132 | 133 | Fix bug in encoding BIT STRINGS where chr() was causing an upgrade to UTF8 134 | 135 | 2003-05-12 18:45 Graham Barr 136 | 137 | * lib/Convert/ASN1.pm: 138 | 139 | Release 0.17 140 | 141 | 2003-05-12 18:45 Graham Barr 142 | 143 | * MANIFEST: 144 | 145 | Add new tests into MANIFEST 146 | 147 | 2003-05-12 18:06 Graham Barr 148 | 149 | * t/funcs.pl: 150 | 151 | Skip rtest if Data::Dumper is not available 152 | 153 | 2003-05-07 16:13 Graham Barr 154 | 155 | * parser.y, lib/Convert/ASN1/parser.pm, t/04opt.t, t/funcs.pl: 156 | 157 | Support OPTIONAL on SET OF and SEQUENCE OF 158 | 159 | 2003-05-07 10:26 Graham Barr 160 | 161 | * lib/Convert/ASN1/_decode.pm, t/00prim.t: 162 | 163 | Fix OID decoding 164 | 165 | 2003-05-06 22:47 Graham Barr 166 | 167 | * t/14any.t: 168 | 169 | Remove duplicate my 170 | 171 | 2003-05-06 22:29 Graham Barr 172 | 173 | * parser.y, lib/Convert/ASN1.pm, lib/Convert/ASN1/_decode.pm, 174 | lib/Convert/ASN1/_encode.pm, lib/Convert/ASN1/parser.pm, t/14any.t: 175 | 176 | Add support for ANY DEFINED BY 177 | Patch from Simon Wilkinson 178 | 179 | 2003-05-06 15:17 Graham Barr 180 | 181 | * lib/Convert/ASN1/Debug.pm: 182 | 183 | Fix debug output for OIDs 184 | 185 | 2003-05-06 13:40 Graham Barr 186 | 187 | * parser.y, lib/Convert/ASN1.pm, lib/Convert/ASN1.pod, 188 | lib/Convert/ASN1/parser.pm, t/08set.t, t/12der.t, t/13utf8.t: 189 | 190 | CER and DER SET encoding 191 | 192 | 2003-05-06 12:07 Graham Barr 193 | 194 | * t/13utf8.t, lib/Convert/ASN1.pm, lib/Convert/ASN1/_decode.pm, 195 | lib/Convert/ASN1/_encode.pm: 196 | 197 | utf8 support for perl >= 5.8 198 | 199 | 2002-08-20 01:00 Graham Barr 200 | 201 | * lib/Convert/ASN1.pm: 202 | 203 | Release 0.16 204 | 205 | 2002-08-20 00:59 Graham Barr 206 | 207 | * README, lib/Convert/ASN1.pod: 208 | 209 | Update search.cpan.org link and add CPAN RT email address 210 | 211 | 2002-08-20 00:53 Graham Barr 212 | 213 | * t/10choice.t: 214 | 215 | Fix test count 216 | 217 | 2002-08-20 00:51 Graham Barr 218 | 219 | * parser.y, lib/Convert/ASN1/_encode.pm, 220 | lib/Convert/ASN1/parser.pm, t/10choice.t: 221 | 222 | Fix for nested CHOICEs and tagged CHOICEs in SEQUENCES 223 | 224 | 2002-03-25 14:59 Graham Barr 225 | 226 | * t/: 00prim.t, 01tag.t, 02seq.t, 03seqof.t, 04opt.t, 05time.t, 227 | 06bigint.t, 08set.t, 09contr.t, 11indef.t: 228 | 229 | Add more detail when tests fail 230 | 231 | 2002-03-25 09:06 Graham Barr 232 | 233 | * lib/Convert/ASN1/_decode.pm, t/10choice.t, t/11indef.t, MANIFEST: 234 | 235 | Patch from Wolfgang Laun 236 | Fix bug in decode when there are nested CHOICEs 237 | Add tests t/10choice.t t/11indef.t 238 | 239 | 2002-03-25 07:46 Graham Barr 240 | 241 | * lib/Convert/: ASN1.pm, ASN1.pod, ASN1/_decode.pm: 242 | 243 | Patch from Wolfgang Laun 244 | Addition of prepare_file and the change prepare to accept a filehandle. 245 | POD updates. 246 | Fix decode of nested indefinate lengths 247 | 248 | 2002-03-25 07:39 Graham Barr 249 | 250 | * mkparse, parser.y, lib/Convert/ASN1/parser.pm: 251 | 252 | Allow '-'s in names and fix an uninit warning in the generated parser 253 | 254 | 2002-02-15 06:51 Graham Barr 255 | 256 | * lib/Convert/ASN1/_encode.pm: 257 | 258 | Use ::isa to determine if stash argument is a HASH 259 | 260 | 2002-02-10 16:41 Graham Barr 261 | 262 | * MANIFEST, examples/x509decode: 263 | 264 | Added x509decode from Norbert Klasen 265 | 266 | 2002-02-10 16:12 Graham Barr 267 | 268 | * lib/Convert/ASN1.pm, lib/Convert/ASN1/_decode.pm, 269 | lib/Convert/ASN1/_encode.pm, t/00prim.t, t/03seqof.t: 270 | 271 | Add support for units with one sinlge, unamed entry 272 | eg test ::= INTEGER or list ::= SEQUENCE OF OCTET STRING 273 | 274 | 2002-01-22 11:24 Graham Barr 275 | 276 | * README, parser.y, lib/Convert/ASN1.pm, lib/Convert/ASN1.pod, 277 | lib/Convert/ASN1/Debug.pm, lib/Convert/ASN1/IO.pm, 278 | lib/Convert/ASN1/_decode.pm, lib/Convert/ASN1/_encode.pm, 279 | lib/Convert/ASN1/parser.pm: 280 | 281 | Release 0.15 282 | 283 | 2002-01-21 20:00 Graham Barr 284 | 285 | * t/06bigint.t: 286 | 287 | Be safer in creating BigInt objects 288 | 289 | 2002-01-02 16:56 Graham Barr 290 | 291 | * lib/Convert/ASN1/_encode.pm: 292 | 293 | Change the encode errors to include the hierarchical name of the 294 | element in the ASN.1 which is causing the problem 295 | 296 | 2002-01-02 16:31 Graham Barr 297 | 298 | * lib/Convert/ASN1.pm: 299 | 300 | Remove unwanted warn statement 301 | 302 | 2001-09-25 00:05 Graham Barr 303 | 304 | * lib/Convert/ASN1.pm: 305 | 306 | Better error reporting for encoding 307 | 308 | 2001-09-22 01:16 Graham Barr 309 | 310 | * parser.y, lib/Convert/ASN1.pm, lib/Convert/ASN1/Debug.pm, 311 | lib/Convert/ASN1/_decode.pm, lib/Convert/ASN1/_encode.pm, 312 | lib/Convert/ASN1/parser.pm, t/00prim.t: 313 | 314 | Add support for RELATIVE-OID 315 | 316 | 2001-09-22 01:14 Graham Barr 317 | 318 | * t/: 00prim.t, 06bigint.t: 319 | 320 | Move some integer tests that really use bigint from 00prim.t into 06bigint.t 321 | 322 | 2001-09-21 23:24 Graham Barr 323 | 324 | * lib/Convert/ASN1.pm, lib/Convert/ASN1/_decode.pm, t/03seqof.t: 325 | 326 | Support for nested SEQUENCE/SET OF's 327 | 328 | 2001-09-10 19:03 Graham Barr 329 | 330 | * README, lib/Convert/ASN1.pm: 331 | 332 | Release 0.14 333 | 334 | 2001-09-10 15:35 Graham Barr 335 | 336 | * MANIFEST, t/08set.t, t/09contr.t: 337 | 338 | Add tests for contructed elements and SETs 339 | 340 | 2001-09-10 15:34 Graham Barr 341 | 342 | * lib/Convert/ASN1/_decode.pm: 343 | 344 | Add support for decoding contructed elements 345 | 346 | 2001-09-07 20:04 Graham Barr 347 | 348 | * lib/Convert/: ASN1.pod, ASN1/_decode.pm: 349 | 350 | Implement the decode of SETs 351 | 352 | 2001-09-06 18:54 Graham Barr 353 | 354 | * lib/Convert/: ASN1.pm, ASN1/Debug.pm: 355 | 356 | Change asn_dump to putput the correct tag number for long tags 357 | and also warn when a length is incorrect 358 | 359 | 2001-09-06 18:41 Graham Barr 360 | 361 | * lib/Convert/ASN1/_decode.pm: 362 | 363 | Fix for indefinite decoding 364 | 365 | 2001-08-26 08:12 Graham Barr 366 | 367 | * lib/Convert/ASN1.pm: 368 | 369 | Release 0.13 370 | 371 | 2001-08-24 15:13 Graham Barr 372 | 373 | * parser.y, lib/Convert/ASN1/parser.pm: 374 | 375 | Remove an unwanted warn statement 376 | 377 | 2001-08-01 19:02 Graham Barr 378 | 379 | * lib/Convert/ASN1/_encode.pm, t/00prim.t: 380 | 381 | Fix boundary condition where we switch over to using Math::BigInt to 382 | encode integers 383 | 384 | 2001-07-31 18:05 Graham Barr 385 | 386 | * MANIFEST, lib/Convert/ASN1.pm: 387 | 388 | Release 0.12 389 | 390 | 2001-07-31 18:04 Graham Barr 391 | 392 | * lib/Convert/ASN1/_encode.pm: 393 | 394 | Make sure value passed to enc_integer is treated as a number and not a string 395 | 396 | 2001-07-31 18:03 Graham Barr 397 | 398 | * parser.y, lib/Convert/ASN1/parser.pm: 399 | 400 | Report which type cannot be found on error 401 | 402 | 2001-06-11 14:13 Graham Barr 403 | 404 | * lib/Convert/ASN1.pm: 405 | 406 | Release 0.11 407 | 408 | 2001-06-11 14:04 Graham Barr 409 | 410 | * lib/Convert/ASN1/_decode.pm, t/02seq.t: 411 | 412 | Fix decode of REAL when it is not the last element of a sequence 413 | 414 | 2001-04-26 07:52 Graham Barr 415 | 416 | * lib/Convert/ASN1.pm, t/05time.t, t/06bigint.t, t/funcs.pl: 417 | 418 | Fix syntax to be compatible with perl5.004_04 419 | Skip bigint tests on 5.004 as they trigger lots of warnings in Math::BigInt 420 | 421 | 2001-04-20 07:26 Graham Barr 422 | 423 | * lib/Convert/ASN1.pm, lib/Convert/ASN1/_encode.pm, t/06bigint.t: 424 | 425 | Fix some bigint encoding problems and add some tests 426 | 427 | 2001-04-20 00:00 Graham Barr 428 | 429 | * Makefile.PL, lib/Convert/ASN1.pm: 430 | 431 | Release 0.09 432 | 433 | 2001-04-19 23:52 Graham Barr 434 | 435 | * MANIFEST, lib/Convert/ASN1.pm, lib/Convert/ASN1.pod, 436 | lib/Convert/ASN1/_decode.pm, lib/Convert/ASN1/_encode.pm, 437 | t/funcs.pl: 438 | 439 | Added support to use Math::BigInt, Thanks to Benjamin Trott 440 | 441 | 2001-04-19 23:50 Graham Barr 442 | 443 | * t/05time.t: 444 | 445 | Tests for UTCTime and GeneralizedTime (The previous was really for bigint tests) 446 | 447 | 2001-04-19 23:32 Graham Barr 448 | 449 | * t/06bigint.t: 450 | 451 | Tests for UTCTime and GeneralizedTime 452 | 453 | 2001-04-19 20:51 Graham Barr 454 | 455 | * lib/Convert/ASN1/IO.pm: 456 | 457 | Fix error message 458 | 459 | 2001-04-19 20:51 Graham Barr 460 | 461 | * lib/Convert/ASN1/_decode.pm: 462 | 463 | Get timezone right when decoding 464 | 465 | 2001-04-19 20:51 Graham Barr 466 | 467 | * t/funcs.pl: 468 | 469 | Useful debug enhancements 470 | 471 | 2001-02-05 22:36 Graham Barr 472 | 473 | * MANIFEST: 474 | 475 | Removed Convert-ASN1.ppd 476 | 477 | 2001-02-05 22:31 Graham Barr 478 | 479 | * lib/Convert/ASN1/parser.pm: 480 | 481 | Added CharacterString UniversalString BMPString 482 | 483 | 2001-01-29 22:35 Graham Barr 484 | 485 | * MANIFEST, OldChanges: 486 | 487 | Move perforce changelog aside and now generate with cvs2cl 488 | 489 | 2000-05-03 13:24 Graham Barr 490 | 491 | * MANIFEST, Makefile.PL, README, mkparse, parser.y, examples/ldap, 492 | examples/ldap-search, lib/Convert/ASN1.pm, lib/Convert/ASN1.pod, 493 | lib/Convert/ASN1/Debug.pm, lib/Convert/ASN1/IO.pm, 494 | lib/Convert/ASN1/_decode.pm, lib/Convert/ASN1/_encode.pm, 495 | lib/Convert/ASN1/parser.pm, t/00prim.t, t/01tag.t, t/02seq.t, 496 | t/03seqof.t, t/04opt.t, t/07io.t, t/funcs.pl: 497 | 498 | Initial revision 499 | 500 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | 5 | 6 | use ExtUtils::MakeMaker; 7 | 8 | my %WriteMakefileArgs = ( 9 | "ABSTRACT" => "Convert between perl data structures and ASN.1 encoded packets", 10 | "AUTHOR" => "Graham Barr ", 11 | "CONFIGURE_REQUIRES" => { 12 | "ExtUtils::MakeMaker" => 0 13 | }, 14 | "DISTNAME" => "Convert-ASN1", 15 | "LICENSE" => "perl", 16 | "NAME" => "Convert::ASN1", 17 | "PREREQ_PM" => {}, 18 | "TEST_REQUIRES" => { 19 | "Math::BigInt" => "1.997", 20 | "Test::More" => "0.90" 21 | }, 22 | "VERSION" => "0.34", 23 | "test" => { 24 | "TESTS" => "t/*.t" 25 | } 26 | ); 27 | 28 | 29 | my %FallbackPrereqs = ( 30 | "Math::BigInt" => "1.997", 31 | "Test::More" => "0.90" 32 | ); 33 | 34 | 35 | unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { 36 | delete $WriteMakefileArgs{TEST_REQUIRES}; 37 | delete $WriteMakefileArgs{BUILD_REQUIRES}; 38 | $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; 39 | } 40 | 41 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 42 | unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; 43 | 44 | WriteMakefile(%WriteMakefileArgs); 45 | -------------------------------------------------------------------------------- /OldChanges: -------------------------------------------------------------------------------- 1 | Change 601 on 2000/09/18 by (Graham Barr) 2 | 3 | Fix typo in configure 4 | 5 | Change 600 on 2000/09/18 by (Graham Barr) 6 | 7 | More robust REAL encode 8 | 9 | Change 561 on 2000/08/14 by (Graham Barr) 10 | 11 | Fix typo in IO.pm 12 | 13 | Change 558 on 2000/08/01 by (Graham Barr) 14 | 15 | Added options for encode/decode of UTCTime and Generalized time 16 | 17 | Change 557 on 2000/08/01 by (Graham Barr) 18 | 19 | Add support to decode indefinate lengths 20 | 21 | Change 556 on 2000/08/01 by (Graham Barr) 22 | 23 | Performance tweaks to IO 24 | 25 | Change 555 on 2000/08/01 by (Graham Barr) 26 | 27 | asn_decode_length(), return a length of -1 for indefinate lengths 28 | 29 | Change 534 on 2000/06/05 by (Graham Barr) 30 | 31 | Another syntax change in Debug.pm for 5.004 support 32 | 33 | Change 531 on 2000/05/30 by (Graham Barr) 34 | 35 | Release 0.07 36 | 37 | Change 530 on 2000/05/30 by (Graham Barr) 38 | 39 | Assert an error if there is nothing to encode for a CHOICE 40 | 41 | Change 528 on 2000/05/22 by (Graham Barr) 42 | 43 | One more for 5.004 44 | 45 | Change 524 on 2000/05/22 by (Graham Barr) 46 | 47 | Release 0.06 48 | 49 | Change 523 on 2000/05/22 by (Graham Barr) 50 | 51 | Now works with 5.004, tested on 5.004, 5.005_03, 5.6.0 52 | 53 | Change 515 on 2000/05/12 by (Graham Barr) 54 | 55 | Release 0.05 56 | 57 | Change 514 on 2000/05/11 by (Graham Barr) 58 | 59 | Fix bug in encoding an integer where one to few bytes would be 60 | encoded causing the sign to change on decode. 61 | 62 | Change 513 on 2000/05/09 by (Graham Barr) 63 | 64 | Fix to asn_read for unit warning when first char of L is > 0x80 65 | 66 | Change 509 on 2000/05/04 by (Graham Barr) 67 | 68 | Convert::ASN1::IO 69 | - fix == instead of = typo 70 | 71 | Change 508 on 2000/05/04 by (Graham Barr) 72 | 73 | Convert::ASN1::IO 74 | - fix bug in asn_read where the FH was passed instead of 75 | the buffer to sysread 76 | - fix bug in asn_recv where we has substr($buf,$len) instead 77 | of substr($buf,0,$len) 78 | - make asn_recv break out of the loop if recv with MSG_PEEK 79 | gives less chars than what was asked for 80 | 81 | Change 497 on 2000/04/28 by (Graham Barr) 82 | 83 | * Fix undef warnings in asn_dump (patch from Chris Ridd 84 | * Change output of asn_dump for tags to [APPLICATION %d], etc 85 | 86 | Change 477 on 2000/04/03 by (Graham Barr) 87 | 88 | Release 0.04 89 | 90 | Change 476 on 2000/03/31 by (Graham Barr) 91 | 92 | More documentation additions 93 | 94 | Change 475 on 2000/03/31 by (Graham Barr) 95 | 96 | Added IO tests 97 | Fixed bugs in asn_read and asn_get 98 | 99 | Change 474 on 2000/03/31 by (Graham Barr) 100 | 101 | - Ensure perl-5.6.0 always works in bytes, except in 102 | _decode_utf8 103 | - Add workaround for bug in 5.6 by mentioning $1..$9 explicitly 104 | in the code 105 | 106 | Change 473 on 2000/03/30 by (Graham Barr) 107 | 108 | Added missing \ to quot $(PERL) 109 | 110 | Change 465 on 2000/03/29 by (Graham Barr) 111 | 112 | Add HTML generation 113 | 114 | Change 423 on 2000/03/28 by (Graham Barr) 115 | 116 | Documentation updates 117 | 118 | Change 406 on 2000/03/28 by (Graham Barr) 119 | 120 | Major code reorg, getting ready for an XS implementation 121 | 122 | Change 405 on 2000/03/27 by (Graham Barr) 123 | 124 | A few more optimization tweaks 125 | Moved utf8 code into ASN1.pm 126 | Added use strict 127 | 128 | Change 404 on 2000/03/24 by (Graham Barr) 129 | 130 | Rework of decoding, now upto 2X faster 131 | 132 | Change 403 on 2000/03/24 by (Graham Barr) 133 | 134 | Check for defined not truth on return from recv() 135 | 136 | Change 396 on 2000/03/22 by (Graham Barr) 137 | 138 | Fix for loops 139 | 140 | Change 386 on 2000/03/21 by (Graham Barr) 141 | 142 | - fix for undef warning in compile_one 143 | - moved options down a level in the object 144 | - added find, to create new objects referencing a given macro 145 | - added ldap example 146 | - add Debug module 147 | - fix for xx OF loop in _decode 148 | - fix typos in IO module 149 | - ensure CONSTRUCTOR bit is set 150 | 151 | Change 384 on 2000/03/18 by (Graham Barr) 152 | 153 | Add encode/decode routines fro UTF8 strings if running 154 | perl 5.6 or later. 155 | 156 | Change 383 on 2000/03/18 by (Graham Barr) 157 | 158 | Now supports hierarchical definition. It is now possible 159 | to copy ASN.1 from rfcs with minimal change 160 | 161 | Change 382 on 2000/03/14 by (Graham Barr) 162 | 163 | Initial version 164 | 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Convert::ASN1 2 | 3 | Convert::ASN1 is a perl library for encoding/decoding data using 4 | ASN.1 definitions 5 | 6 | The ASN.1 parser is not a complete implementation of the 7 | [ASN.1](http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf) 8 | specification. It has been built over time and features have been 9 | added on an as-needed basis. 10 | 11 | ## Latest Release 12 | 13 | The latest release can be found on http://www.cpan.org/ at 14 | http://search.cpan.org/dist/Convert-ASN1/ 15 | 16 | The documentation is at http://search.cpan.org/perldoc?Convert::ASN1 17 | 18 | ## Installing 19 | 20 | Install with your favorite CPAN install manager, eg 21 | 22 | cpanm Convert::ASN1 23 | 24 | If you do not have cpanm installed you can run 25 | 26 | curl -s -L http://cpanmin.us | perl - Convert::ASN1 27 | 28 | ## Contributing 29 | 30 | ### Git 31 | 32 | The preferred method of contribution is by forking a repository on 33 | github. 34 | 35 | If you are not familiar with working with forked repositories please 36 | read http://help.github.com/fork-a-repo/ for details on how to setup 37 | your fork. 38 | 39 | Try to avoid submitting to the master branch in your fork, it is 40 | useful to keep that following the main repository and if I decide 41 | to cherry-pick or fixup any commit you submit in a pull request you 42 | will have tracking issues later 43 | 44 | To start a branch for fixes do the following, assuming you have the 45 | origin and upstream remotes setup as in the guide linked to above. 46 | 47 | git fetch upstream git checkout -b mybranch upstream/master 48 | 49 | this will checkout a new branch called _mybranch_ from the latest 50 | code in the master branch of the upstream repository. 51 | 52 | Once you have finished push that branch to your origin repository 53 | with 54 | 55 | git push -u origin HEAD 56 | 57 | The -u will setup branch tracking so if you later add more commits 58 | a simple 59 | 60 | git push 61 | 62 | is enough to push those commits. 63 | 64 | Once you have pushed the branch to github, send a pull as described 65 | at http://help.github.com/send-pull-requests/ 66 | 67 | ### Dist::Zilla 68 | 69 | The release is developed using 70 | [Dist::Zilla](http://search.cpan.org/perldoc?Dist::Zilla) 71 | 72 | you will need to install 73 | 74 | cpanm Dist::Zilla 75 | 76 | once you have the base install of Dist::Zilla run 77 | 78 | dzil authordeps --missing | cpanm dzil listdeps --missing | cpanm 79 | 80 | ### perl-byacc 81 | 82 | If you need to make changes to the parser then you will need to 83 | build perl-byacc1.8.2. You can fetch the source from 84 | [perl-byacc1.8.2.tar.gz](http://www.cpan.org/src/misc/perl-byacc1.8.2.tar.gz) 85 | 86 | With that built and available in your $PATH as byacc the parser can 87 | be compiled with 88 | 89 | perl mkparse parser.y lib/Convert/ASN1/parser.pm 90 | 91 | ## License 92 | 93 | This software is copyright (c) 2000-2012 by Graham Barr. 94 | 95 | This is free software; you can redistribute it and/or modify it under 96 | the same terms as the Perl 5 programming language system itself. 97 | 98 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. To change prereqs, edit the `dist.ini` file. 2 | 3 | 4 | on 'test' => sub { 5 | requires "Math::BigInt" => "1.997"; 6 | requires "Test::More" => "0.90"; 7 | }; 8 | 9 | on 'configure' => sub { 10 | requires "ExtUtils::MakeMaker" => "0"; 11 | }; 12 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = Convert-ASN1 2 | author = Graham Barr 3 | license = Perl_5 4 | copyright_holder = Graham Barr 5 | abstract = Convert between perl data structures and ASN.1 encoded packets 6 | [Meta::Maintainers] 7 | maintainer = Timothy Legge 8 | 9 | 10 | [@Filter] 11 | :version = 4.1 ; Dist::Zilla version needed 12 | bundle = @Basic 13 | remove = GatherDir 14 | remove = Readme 15 | 16 | [MetaNoIndex] 17 | directory = lib/Convert/ASN1 18 | directory = t 19 | directory = examples 20 | 21 | [Git::GatherDir] 22 | exclude_filename = cpanfile 23 | exclude_filename = Makefile.PL 24 | [Git::NextVersion] 25 | 26 | [PkgVersion] 27 | [PodVersion] 28 | 29 | [Prereqs / TestRequires] 30 | Test::More = 0.90 31 | Math::BigInt = 1.997 32 | 33 | [NextRelease] 34 | format = %v -- %{EEE MMM dd HH:mm:ss VVV yyyy}d 35 | filename = ChangeLog 36 | 37 | [MetaJSON] 38 | [MetaResources] 39 | bugtracker.web = https://github.com/gbarr/perl-Convert-ASN1/issues 40 | repository.web = https://github.com/gbarr/perl-Convert-ASN1 41 | repository.url = git://github.com/gbarr/perl-Convert-ASN1.git 42 | repository.type = git 43 | 44 | [CPANFile] 45 | 46 | [CopyFilesFromBuild::Filtered] 47 | copy = cpanfile 48 | copy = Makefile.PL 49 | 50 | [CopyFilesFromRelease] 51 | copy = cpanfile, Makefile.PL 52 | 53 | [Signature] 54 | 55 | [@Git] 56 | changelog = ChangeLog 57 | -------------------------------------------------------------------------------- /examples/ldap: -------------------------------------------------------------------------------- 1 | $desc = <<'ESQ'; 2 | LDAPMessage ::= SEQUENCE { 3 | messageID MessageID, 4 | protocolOp CHOICE { 5 | bindRequest BindRequest, 6 | bindResponse BindResponse, 7 | unbindRequest UnbindRequest, 8 | searchRequest SearchRequest, 9 | searchResEntry SearchResultEntry, 10 | searchResDone SearchResultDone, 11 | searchResRef SearchResultReference, 12 | modifyRequest ModifyRequest, 13 | modifyResponse ModifyResponse, 14 | addRequest AddRequest, 15 | addResponse AddResponse, 16 | delRequest DelRequest, 17 | delResponse DelResponse, 18 | modDNRequest ModifyDNRequest, 19 | modDNResponse ModifyDNResponse, 20 | compareRequest CompareRequest, 21 | compareResponse CompareResponse, 22 | abandonRequest AbandonRequest, 23 | extendedReq ExtendedRequest, 24 | extendedResp ExtendedResponse } 25 | controls [0] Controls OPTIONAL } 26 | 27 | MessageID ::= INTEGER -- (0 .. maxInt) 28 | 29 | -- maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- 30 | 31 | LDAPString ::= OCTET STRING 32 | 33 | LDAPOID ::= OCTET STRING 34 | 35 | LDAPDN ::= LDAPString 36 | 37 | RelativeLDAPDN ::= LDAPString 38 | 39 | AttributeType ::= LDAPString 40 | 41 | AttributeDescription ::= LDAPString 42 | 43 | AttributeDescriptionList ::= SEQUENCE OF 44 | AttributeDescription 45 | 46 | AttributeValue ::= OCTET STRING 47 | 48 | AttributeValueAssertion ::= SEQUENCE { 49 | attributeDesc AttributeDescription, 50 | assertionValue AssertionValue } 51 | 52 | AssertionValue ::= OCTET STRING 53 | 54 | Attribute ::= SEQUENCE { 55 | type AttributeDescription, 56 | vals SET OF AttributeValue } 57 | 58 | MatchingRuleId ::= LDAPString 59 | 60 | LDAPResult ::= SEQUENCE { 61 | resultCode ENUMERATED { 62 | success (0), 63 | operationsError (1), 64 | protocolError (2), 65 | timeLimitExceeded (3), 66 | sizeLimitExceeded (4), 67 | compareFalse (5), 68 | compareTrue (6), 69 | authMethodNotSupported (7), 70 | strongAuthRequired (8), 71 | -- 9 reserved -- 72 | referral (10), -- new 73 | adminLimitExceeded (11), -- new 74 | unavailableCriticalExtension (12), -- new 75 | confidentialityRequired (13), -- new 76 | saslBindInProgress (14), -- new 77 | noSuchAttribute (16), 78 | undefinedAttributeType (17), 79 | inappropriateMatching (18), 80 | constraintViolation (19), 81 | attributeOrValueExists (20), 82 | invalidAttributeSyntax (21), 83 | -- 22-31 unused -- 84 | noSuchObject (32), 85 | aliasProblem (33), 86 | invalidDNSyntax (34), 87 | -- 35 reserved for undefined isLeaf -- 88 | aliasDereferencingProblem (36), 89 | -- 37-47 unused -- 90 | inappropriateAuthentication (48), 91 | invalidCredentials (49), 92 | insufficientAccessRights (50), 93 | busy (51), 94 | unavailable (52), 95 | unwillingToPerform (53), 96 | loopDetect (54), 97 | -- 55-63 unused -- 98 | namingViolation (64), 99 | objectClassViolation (65), 100 | notAllowedOnNonLeaf (66), 101 | notAllowedOnRDN (67), 102 | entryAlreadyExists (68), 103 | objectClassModsProhibited (69), 104 | -- 70 reserved for CLDAP -- 105 | affectsMultipleDSAs (71), -- new 106 | -- 72-79 unused -- 107 | other (80)} 108 | -- 81-90 reserved for APIs -- 109 | matchedDN LDAPDN, 110 | errorMessage LDAPString, 111 | referral [3] Referral OPTIONAL } 112 | 113 | Referral ::= SEQUENCE OF LDAPURL 114 | 115 | LDAPURL ::= LDAPString -- limited to characters permitted in URLs 116 | 117 | Controls ::= SEQUENCE OF Control 118 | 119 | Control ::= SEQUENCE { 120 | controlType LDAPOID, 121 | criticality BOOLEAN , -- DEFAULT FALSE, 122 | controlValue OCTET STRING OPTIONAL } 123 | 124 | BindRequest ::= [APPLICATION 0] SEQUENCE { 125 | version INTEGER, -- (1 .. 127), 126 | name LDAPDN, 127 | authentication AuthenticationChoice } 128 | 129 | AuthenticationChoice ::= CHOICE { 130 | simple [0] OCTET STRING, 131 | -- 1 and 2 reserved 132 | sasl [3] SaslCredentials } 133 | 134 | SaslCredentials ::= SEQUENCE { 135 | mechanism LDAPString, 136 | credentials OCTET STRING OPTIONAL } 137 | 138 | BindResponse ::= [APPLICATION 1] SEQUENCE { 139 | COMPONENTS OF LDAPResult, 140 | serverSaslCreds [7] OCTET STRING OPTIONAL } 141 | 142 | UnbindRequest ::= [APPLICATION 2] NULL 143 | 144 | SearchRequest ::= [APPLICATION 3] SEQUENCE { 145 | baseObject LDAPDN, 146 | scope ENUMERATED { 147 | baseObject (0), 148 | singleLevel (1), 149 | wholeSubtree (2) } 150 | derefAliases ENUMERATED { 151 | neverDerefAliases (0), 152 | derefInSearching (1), 153 | derefFindingBaseObj (2), 154 | derefAlways (3) } 155 | sizeLimit INTEGER , -- (0 .. maxInt), 156 | timeLimit INTEGER , -- (0 .. maxInt), 157 | typesOnly BOOLEAN, 158 | filter Filter, 159 | attributes AttributeDescriptionList } 160 | 161 | Filter ::= CHOICE { 162 | and [0] SET OF Filter, 163 | or [1] SET OF Filter, 164 | not [2] Filter, 165 | equalityMatch [3] AttributeValueAssertion, 166 | substrings [4] SubstringFilter, 167 | greaterOrEqual [5] AttributeValueAssertion, 168 | lessOrEqual [6] AttributeValueAssertion, 169 | present [7] AttributeDescription, 170 | approxMatch [8] AttributeValueAssertion, 171 | extensibleMatch [9] MatchingRuleAssertion } 172 | 173 | SubstringFilter ::= SEQUENCE { 174 | type AttributeDescription, 175 | -- at least one must be present 176 | substrings SEQUENCE OF CHOICE { 177 | initial [0] LDAPString, 178 | any [1] LDAPString, 179 | final [2] LDAPString } } 180 | 181 | MatchingRuleAssertion ::= SEQUENCE { 182 | matchingRule [1] MatchingRuleId OPTIONAL, 183 | type [2] AttributeDescription OPTIONAL, 184 | matchValue [3] AssertionValue, 185 | dnAttributes [4] BOOLEAN } -- DEFAULT FALSE } 186 | 187 | SearchResultEntry ::= [APPLICATION 4] SEQUENCE { 188 | objectName LDAPDN, 189 | attributes PartialAttributeList } 190 | 191 | PartialAttributeList ::= SEQUENCE OF SEQUENCE { 192 | type AttributeDescription, 193 | vals SET OF AttributeValue } 194 | 195 | SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL 196 | 197 | SearchResultDone ::= [APPLICATION 5] LDAPResult 198 | 199 | ModifyRequest ::= [APPLICATION 6] SEQUENCE { 200 | object LDAPDN, 201 | modification SEQUENCE OF SEQUENCE { 202 | operation ENUMERATED { 203 | add (0), 204 | delete (1), 205 | replace (2) } 206 | modification AttributeTypeAndValues } } 207 | 208 | AttributeTypeAndValues ::= SEQUENCE { 209 | type AttributeDescription, 210 | vals SET OF AttributeValue } 211 | 212 | ModifyResponse ::= [APPLICATION 7] LDAPResult 213 | 214 | AddRequest ::= [APPLICATION 8] SEQUENCE { 215 | entry LDAPDN, 216 | attributes AttributeList } 217 | 218 | AttributeList ::= SEQUENCE OF SEQUENCE { 219 | type AttributeDescription, 220 | vals SET OF AttributeValue } 221 | 222 | AddResponse ::= [APPLICATION 9] LDAPResult 223 | 224 | DelRequest ::= [APPLICATION 10] LDAPDN 225 | 226 | DelResponse ::= [APPLICATION 11] LDAPResult 227 | 228 | ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { 229 | entry LDAPDN, 230 | newrdn RelativeLDAPDN, 231 | deleteoldrdn BOOLEAN, 232 | newSuperior [0] LDAPDN OPTIONAL } 233 | 234 | ModifyDNResponse ::= [APPLICATION 13] LDAPResult 235 | 236 | CompareRequest ::= [APPLICATION 14] SEQUENCE { 237 | entry LDAPDN, 238 | ava AttributeValueAssertion } 239 | 240 | CompareResponse ::= [APPLICATION 15] LDAPResult 241 | 242 | AbandonRequest ::= [APPLICATION 16] MessageID 243 | 244 | ExtendedRequest ::= [APPLICATION 23] SEQUENCE { 245 | requestName [0] LDAPOID, 246 | requestValue [1] OCTET STRING OPTIONAL } 247 | 248 | ExtendedResponse ::= [APPLICATION 24] SEQUENCE { 249 | COMPONENTS OF LDAPResult, 250 | responseName [10] LDAPOID OPTIONAL, 251 | response [11] OCTET STRING OPTIONAL } 252 | 253 | 254 | VirtualListViewRequest ::= SEQUENCE { 255 | beforeCount INTEGER , --(0 .. maxInt), 256 | afterCount INTEGER , --(0 .. maxInt), 257 | CHOICE { 258 | byIndex [0] SEQUENCE { 259 | index INTEGER , --(0 .. maxInt), 260 | contentCount INTEGER } --(0 .. maxInt) } 261 | byValue [1] AssertionValue } 262 | -- byValue [1] greaterThanOrEqual assertionValue } 263 | contextID OCTET STRING OPTIONAL } 264 | 265 | VirtualListViewResponse ::= SEQUENCE { 266 | targetPosition INTEGER , --(0 .. maxInt), 267 | contentCount INTEGER , --(0 .. maxInt), 268 | virtualListViewResult ENUMERATED { 269 | success (0), 270 | operatonsError (1), 271 | unwillingToPerform (53), 272 | insufficientAccessRights (50), 273 | busy (51), 274 | timeLimitExceeded (3), 275 | adminLimitExceeded (11), 276 | sortControlMissing (60), 277 | indexRangeError (61), 278 | other (80) } } 279 | ESQ 280 | 281 | use lib 'lib'; 282 | use Convert::ASN1 qw(:debug); 283 | use Convert::ASN1::Debug qw(asn_dump asn_hexdump); 284 | $asn = Convert::ASN1->new; 285 | $asn->prepare($desc) or die $asn->error; 286 | #$asn->dump; 287 | 288 | $filter = $asn->find('Filter'); 289 | 290 | # A Filter 291 | # (&(!(desc=value))(|(xx=x*y*)(yy=*1*2))) 292 | 293 | $buf = $filter->encode( 294 | { 295 | and => [ 296 | { 297 | not => { 298 | equalityMatch => { 299 | attributeDesc => 'desc', 300 | assertionValue => 'value' 301 | } 302 | } 303 | }, 304 | { 305 | or => [ 306 | { 307 | substrings => { 308 | type => 'xx', 309 | substrings => [ 310 | { 311 | initial => 'x' 312 | }, 313 | { 314 | any => 'y' 315 | } 316 | ] 317 | } 318 | }, 319 | { 320 | substrings => { 321 | type => 'yy', 322 | substrings => [ 323 | { 324 | any => 1 325 | }, 326 | { 327 | final => 2 328 | } 329 | ] 330 | } 331 | } 332 | ] 333 | } 334 | ] 335 | } 336 | ) or die $filter->error; 337 | 338 | asn_dump($buf); 339 | 340 | $ret = $filter->decode($buf) or die $filter->error; 341 | use Data::Dumper; 342 | $Data::Dumper::Indent=1; 343 | $Data::Dumper::Quotekeys=0; 344 | print Dumper($ret); 345 | -------------------------------------------------------------------------------- /examples/ldap-search: -------------------------------------------------------------------------------- 1 | # This is an example of a search PDU from the LDAP protocol 2 | 3 | use Convert::ASN1; 4 | use Data::Dumper; 5 | 6 | my %scope = qw(base 0 one 1 single 1 sub 2 subtree 2); 7 | my %deref = qw(never 0 search 1 find 2 always 3); 8 | 9 | my $search_pdu = Convert::ASN1->new; 10 | $search_pdu->prepare(q( 11 | SEQUENCE { 12 | mesgid INTEGER, 13 | [APPLICATION 3] SEQUENCE { 14 | base STRING, 15 | scope ENUM, 16 | deref ENUM, 17 | sizeLimit INTEGER, 18 | timeLimit INTEGER, 19 | typesOnly BOOLEAN, 20 | filter STRING, -- for test, should be ANY and the var should hold 21 | -- a pre-encoded filter 22 | attrs SEQUENCE OF STRING 23 | } 24 | control [0] SEQUENCE OF SEQUENCE { -- this should be optional but we cannot do that inline 25 | type STRING, 26 | critical BOOLEAN, 27 | value STRING OPTIONAL 28 | } 29 | } 30 | )) or die $search_pdu->error; 31 | 32 | $buf = $search_pdu->encode( 33 | mesgid => 3, 34 | base => "cn=base", 35 | scope => $scope{one}, 36 | deref => $deref{find}, 37 | sizeLimit => 0, 38 | timeLimit => 0, 39 | typesOnly => 0, 40 | filter => "A filter", 41 | attrs => [qw(cn postalAddress)], 42 | control => [ 43 | { 44 | type => "1.2.3.4", 45 | critical => 1 46 | }, 47 | { 48 | type => "9.8.7.6", 49 | critical => 0, 50 | value => "abc" 51 | } 52 | ] 53 | ); 54 | 55 | $Data::Dumper::Indent = 1; 56 | print Dumper( $search_pdu->decode($buf)); 57 | 58 | $h = unpack("H*",$buf); 59 | $h =~ s/(..)/$1 /g; 60 | $h =~ s/(.{47}\s)/$1\n/g; 61 | print $h,"\n"; 62 | -------------------------------------------------------------------------------- /examples/x509decode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # $Id: x509decode,v 1.1 2002/02/10 16:41:28 gbarr Exp $ 3 | # (c) 2001-2002 Norbert Klasen, DAASI International GmbH. All rights reserved. 4 | # This package is free software; you can redistribute it and/or 5 | # modify it under the same terms as Perl itself. 6 | # 7 | # decode X.509 certificates 8 | # 9 | # varable naming 10 | # Convert::ASN1 objects are prefixed with asn_ 11 | # variables holding binary DER content are prefixed with der_ 12 | 13 | use strict; 14 | 15 | use Data::Dumper; 16 | $Data::Dumper::Indent=1; 17 | $Data::Dumper::Quotekeys=1; 18 | $Data::Dumper::Useqq=1; 19 | 20 | use Convert::ASN1 qw(:io :debug); 21 | 22 | # parse ASN.1 desciptions 23 | my $asn = Convert::ASN1->new; 24 | $asn->prepare(<error; 25 | -- ASN.1 from RFC2459 and X.509(2001) 26 | -- Adapted for use with Convert::ASN1 27 | 28 | -- attribute data types -- 29 | 30 | Attribute ::= SEQUENCE { 31 | type AttributeType, 32 | values SET OF AttributeValue 33 | -- at least one value is required -- 34 | } 35 | 36 | AttributeType ::= OBJECT IDENTIFIER 37 | 38 | AttributeValue ::= DirectoryString --ANY 39 | 40 | AttributeTypeAndValue ::= SEQUENCE { 41 | type AttributeType, 42 | value AttributeValue 43 | } 44 | 45 | 46 | -- naming data types -- 47 | 48 | Name ::= CHOICE { -- only one possibility for now 49 | rdnSequence RDNSequence 50 | } 51 | 52 | RDNSequence ::= SEQUENCE OF RelativeDistinguishedName 53 | 54 | DistinguishedName ::= RDNSequence 55 | 56 | RelativeDistinguishedName ::= 57 | SET OF AttributeTypeAndValue --SET SIZE (1 .. MAX) OF 58 | 59 | 60 | -- Directory string type -- 61 | 62 | DirectoryString ::= CHOICE { 63 | teletexString TeletexString, --(SIZE (1..MAX)), 64 | printableString PrintableString, --(SIZE (1..MAX)), 65 | bmpString BMPString, --(SIZE (1..MAX)), 66 | universalString UniversalString, --(SIZE (1..MAX)), 67 | utf8String UTF8String, --(SIZE (1..MAX)), 68 | ia5String IA5String --added for EmailAddress 69 | } 70 | 71 | 72 | -- certificate and CRL specific structures begin here 73 | 74 | Certificate ::= SEQUENCE { 75 | tbsCertificate TBSCertificate, 76 | signatureAlgorithm AlgorithmIdentifier, 77 | signature BIT STRING 78 | } 79 | 80 | TBSCertificate ::= SEQUENCE { 81 | version [0] EXPLICIT Version OPTIONAL, --DEFAULT v1 82 | serialNumber CertificateSerialNumber, 83 | signature AlgorithmIdentifier, 84 | issuer Name, 85 | validity Validity, 86 | subject Name, 87 | subjectPublicKeyInfo SubjectPublicKeyInfo, 88 | issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 89 | -- If present, version shall be v2 or v3 90 | subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 91 | -- If present, version shall be v2 or v3 92 | extensions [3] EXPLICIT Extensions OPTIONAL 93 | -- If present, version shall be v3 94 | } 95 | 96 | Version ::= INTEGER --{ v1(0), v2(1), v3(2) } 97 | 98 | CertificateSerialNumber ::= INTEGER 99 | 100 | Validity ::= SEQUENCE { 101 | notBefore Time, 102 | notAfter Time 103 | } 104 | 105 | Time ::= CHOICE { 106 | utcTime UTCTime, 107 | generalTime GeneralizedTime 108 | } 109 | 110 | UniqueIdentifier ::= BIT STRING 111 | 112 | SubjectPublicKeyInfo ::= SEQUENCE { 113 | algorithm AlgorithmIdentifier, 114 | subjectPublicKey BIT STRING 115 | } 116 | 117 | Extensions ::= SEQUENCE OF Extension --SIZE (1..MAX) OF Extension 118 | 119 | Extension ::= SEQUENCE { 120 | extnID OBJECT IDENTIFIER, 121 | critical BOOLEAN OPTIONAL, --DEFAULT FALSE, 122 | extnValue OCTET STRING 123 | } 124 | 125 | AlgorithmIdentifier ::= SEQUENCE { 126 | algorithm OBJECT IDENTIFIER, 127 | parameters ANY OPTIONAL 128 | } 129 | 130 | 131 | --extensions 132 | 133 | AuthorityKeyIdentifier ::= SEQUENCE { 134 | keyIdentifier [0] KeyIdentifier OPTIONAL, 135 | authorityCertIssuer [1] GeneralNames OPTIONAL, 136 | authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } 137 | -- authorityCertIssuer and authorityCertSerialNumber shall both 138 | -- be present or both be absent 139 | 140 | KeyIdentifier ::= OCTET STRING 141 | 142 | SubjectKeyIdentifier ::= KeyIdentifier 143 | 144 | 145 | -- key usage extension OID and syntax 146 | -- id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } 147 | 148 | KeyUsage ::= BIT STRING --{ 149 | -- digitalSignature (0), 150 | -- nonRepudiation (1), 151 | -- keyEncipherment (2), 152 | -- dataEncipherment (3), 153 | -- keyAgreement (4), 154 | -- keyCertSign (5), 155 | -- cRLSign (6), 156 | -- encipherOnly (7), 157 | -- decipherOnly (8) } 158 | 159 | 160 | -- private key usage period extension OID and syntax 161 | -- id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } 162 | 163 | PrivateKeyUsagePeriod ::= SEQUENCE { 164 | notBefore [0] GeneralizedTime OPTIONAL, 165 | notAfter [1] GeneralizedTime OPTIONAL } 166 | -- either notBefore or notAfter shall be present 167 | 168 | 169 | -- certificate policies extension OID and syntax 170 | -- id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } 171 | 172 | CertificatePolicies ::= SEQUENCE OF PolicyInformation 173 | 174 | PolicyInformation ::= SEQUENCE { 175 | policyIdentifier CertPolicyId, 176 | policyQualifiers SEQUENCE OF 177 | PolicyQualifierInfo } --OPTIONAL } 178 | 179 | CertPolicyId ::= OBJECT IDENTIFIER 180 | 181 | PolicyQualifierInfo ::= SEQUENCE { 182 | policyQualifierId PolicyQualifierId, 183 | qualifier ANY } --DEFINED BY policyQualifierId } 184 | 185 | -- Implementations that recognize additional policy qualifiers shall 186 | -- augment the following definition for PolicyQualifierId 187 | 188 | PolicyQualifierId ::= 189 | OBJECT IDENTIFIER --( id-qt-cps | id-qt-unotice ) 190 | 191 | -- CPS pointer qualifier 192 | 193 | CPSuri ::= IA5String 194 | 195 | -- user notice qualifier 196 | 197 | UserNotice ::= SEQUENCE { 198 | noticeRef NoticeReference OPTIONAL, 199 | explicitText DisplayText OPTIONAL} 200 | 201 | NoticeReference ::= SEQUENCE { 202 | organization DisplayText, 203 | noticeNumbers SEQUENCE OF INTEGER } 204 | 205 | DisplayText ::= CHOICE { 206 | visibleString VisibleString , 207 | bmpString BMPString , 208 | utf8String UTF8String } 209 | 210 | 211 | -- policy mapping extension OID and syntax 212 | -- id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } 213 | 214 | PolicyMappings ::= SEQUENCE OF SEQUENCE { 215 | issuerDomainPolicy CertPolicyId, 216 | subjectDomainPolicy CertPolicyId } 217 | 218 | 219 | -- subject alternative name extension OID and syntax 220 | -- id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } 221 | 222 | SubjectAltName ::= GeneralNames 223 | 224 | GeneralNames ::= SEQUENCE OF GeneralName 225 | 226 | GeneralName ::= CHOICE { 227 | otherName [0] AnotherName, 228 | rfc822Name [1] IA5String, 229 | dNSName [2] IA5String, 230 | x400Address [3] ANY, --ORAddress, 231 | directoryName [4] Name, 232 | ediPartyName [5] EDIPartyName, 233 | uniformResourceIdentifier [6] IA5String, 234 | iPAddress [7] OCTET STRING, 235 | registeredID [8] OBJECT IDENTIFIER } 236 | 237 | -- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as 238 | -- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax 239 | 240 | AnotherName ::= SEQUENCE { 241 | type OBJECT IDENTIFIER, 242 | value [0] EXPLICIT ANY } --DEFINED BY type-id } 243 | 244 | EDIPartyName ::= SEQUENCE { 245 | nameAssigner [0] DirectoryString OPTIONAL, 246 | partyName [1] DirectoryString } 247 | 248 | 249 | -- issuer alternative name extension OID and syntax 250 | -- id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } 251 | 252 | IssuerAltName ::= GeneralNames 253 | 254 | 255 | -- id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } 256 | 257 | SubjectDirectoryAttributes ::= SEQUENCE OF Attribute 258 | 259 | 260 | -- basic constraints extension OID and syntax 261 | -- id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } 262 | 263 | BasicConstraints ::= SEQUENCE { 264 | cA BOOLEAN OPTIONAL, --DEFAULT FALSE, 265 | pathLenConstraint INTEGER OPTIONAL } 266 | 267 | 268 | -- name constraints extension OID and syntax 269 | -- id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } 270 | 271 | NameConstraints ::= SEQUENCE { 272 | permittedSubtrees [0] GeneralSubtrees OPTIONAL, 273 | excludedSubtrees [1] GeneralSubtrees OPTIONAL } 274 | 275 | GeneralSubtrees ::= SEQUENCE OF GeneralSubtree 276 | 277 | GeneralSubtree ::= SEQUENCE { 278 | base GeneralName, 279 | minimum [0] BaseDistance OPTIONAL, --DEFAULT 0, 280 | maximum [1] BaseDistance OPTIONAL } 281 | 282 | BaseDistance ::= INTEGER 283 | 284 | 285 | -- policy constraints extension OID and syntax 286 | -- id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } 287 | 288 | PolicyConstraints ::= SEQUENCE { 289 | requireExplicitPolicy [0] SkipCerts OPTIONAL, 290 | inhibitPolicyMapping [1] SkipCerts OPTIONAL } 291 | 292 | SkipCerts ::= INTEGER 293 | 294 | 295 | -- CRL distribution points extension OID and syntax 296 | -- id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} 297 | 298 | cRLDistributionPoints ::= SEQUENCE OF DistributionPoint 299 | 300 | DistributionPoint ::= SEQUENCE { 301 | distributionPoint [0] DistributionPointName OPTIONAL, 302 | reasons [1] ReasonFlags OPTIONAL, 303 | cRLIssuer [2] GeneralNames OPTIONAL } 304 | 305 | DistributionPointName ::= CHOICE { 306 | fullName [0] GeneralNames, 307 | nameRelativeToCRLIssuer [1] RelativeDistinguishedName } 308 | 309 | ReasonFlags ::= BIT STRING --{ 310 | -- unused (0), 311 | -- keyCompromise (1), 312 | -- cACompromise (2), 313 | -- affiliationChanged (3), 314 | -- superseded (4), 315 | -- cessationOfOperation (5), 316 | -- certificateHold (6), 317 | -- privilegeWithdrawn (7), 318 | -- aACompromise (8) } 319 | 320 | 321 | -- extended key usage extension OID and syntax 322 | -- id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} 323 | 324 | ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId 325 | 326 | KeyPurposeId ::= OBJECT IDENTIFIER 327 | 328 | -- extended key purpose OIDs 329 | -- id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } 330 | -- id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } 331 | -- id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } 332 | -- id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } 333 | -- id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } 334 | -- id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } 335 | -- id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } 336 | -- id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } 337 | ASN1 338 | 339 | # decoders for basic types 340 | my $asn_BitString = Convert::ASN1->new(); 341 | $asn_BitString->prepare("bitString BIT STRING"); 342 | 343 | my $asn_OctetString = Convert::ASN1->new(); 344 | $asn_OctetString->prepare("octetString OCTET STRING"); 345 | 346 | # decoders for extensions 347 | my %extnoid2asn = ( 348 | '2.5.29.9' => $asn->find('SubjectDirectoryAttributes'), 349 | '2.5.29.14' => $asn_OctetString, #'SubjectKeyIdentifier', 350 | '2.5.29.15' => $asn_BitString, #'keyUsage', 351 | '2.5.29.16' => $asn->find('PrivateKeyUsagePeriod'), 352 | '2.5.29.17' => $asn->find('SubjectAltName'), 353 | '2.5.29.18' => $asn->find('IssuerAltName'), 354 | '2.5.29.19' => $asn->find('BasicConstraints'), 355 | # '2.5.29.20' => 'cRLNumber', 356 | # '2.5.29.21' => 'cRLReasons', 357 | # '2.5.29.23' => 'holdInstructionCode', 358 | # '2.5.29.24' => 'invalidityDate', 359 | # '2.5.29.27' => 'deltaCRLIndicator', 360 | # '2.5.29.28' => 'issuingDistributionPoint', 361 | # '2.5.29.29' => 'certificateIssuer', 362 | '2.5.29.30' => $asn->find('NameConstraints'), 363 | '2.5.29.31' => $asn->find('cRLDistributionPoints'), 364 | '2.5.29.32' => $asn->find('CertificatePolicies'), 365 | '2.5.29.33' => $asn->find('PolicyMappings'), 366 | '2.5.29.35' => $asn->find('AuthorityKeyIdentifier'), 367 | '2.5.29.36' => $asn->find('PolicyConstraints'), 368 | '2.5.29.37' => $asn->find('ExtKeyUsageSyntax'), 369 | # '2.5.29.40' => 'cRLStreamIdentifier', 370 | # '2.5.29.44' => 'cRLScope', 371 | # '2.5.29.45' => 'statusReferrals', 372 | # '2.5.29.46' => 'freshestCRL', 373 | # '2.5.29.47' => 'orderedList', 374 | # '2.5.29.51' => 'baseUpdateTime', 375 | # '2.5.29.53' => 'deltaInfo', 376 | # '2.5.29.54' => 'inhibitAnyPolicy', 377 | # netscape-cert-extensions 378 | '2.16.840.1.113730.1.1' => $asn_BitString, # netscape-cert-type 379 | '2.16.840.1.113730.1.2' => $asn->find('DirectoryString'), # netscape-base-url 380 | '2.16.840.1.113730.1.3' => $asn->find('DirectoryString'), # netscape-revocation-url 381 | '2.16.840.1.113730.1.4' => $asn->find('DirectoryString'), # netscape-ca-revocation-url 382 | '2.16.840.1.113730.1.7' => $asn->find('DirectoryString'), # netscape-cert-renewal-url 383 | '2.16.840.1.113730.1.8' => $asn->find('DirectoryString'), # netscape-ca-policy-url 384 | '2.16.840.1.113730.1.12' => $asn->find('DirectoryString'), # netscape-ssl-server-name 385 | '2.16.840.1.113730.1.13' => $asn->find('DirectoryString'), # netscape-comment 386 | ); 387 | 388 | my $asn_cert = $asn->find('Certificate'); 389 | 390 | while ( my $filename = shift ) { 391 | my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, 392 | $atime,$mtime,$ctime,$blksize,$blocks) = stat $filename; 393 | open FILE, "<$filename" or die "no such file"; 394 | binmode FILE; 395 | my $der_cert; 396 | read FILE, $der_cert, $size; 397 | close FILE; 398 | decodeCert( $der_cert ); 399 | } 400 | 401 | sub decodeCert { 402 | my $der_cert = shift; 403 | #asn_dump( $der_cert ); 404 | 405 | my $cert = $asn_cert->decode($der_cert) or die $asn_cert->error; 406 | 407 | #extensions 408 | foreach my $extension ( @{$cert->{'tbsCertificate'}->{'extensions'}} ) { 409 | #print "extension: ", $oid2extension{$extension->{'extnID'}}, "\n"; 410 | if ( exists $extnoid2asn{$extension->{'extnID'}} ) { 411 | $extension->{'extnValue'} = ($extnoid2asn{$extension->{'extnID'}})->decode( $extension->{'extnValue'} ); 412 | } else { 413 | print STDERR "unknown ", $extension->{'critical'} ? "critical " : "", "extension: ", $extension->{'extnID'}, "\n"; 414 | asn_dump( $extension->{'extnValue'} ); 415 | } 416 | } 417 | 418 | print Dumper( $cert ); 419 | } 420 | -------------------------------------------------------------------------------- /lib/Convert/ASN1.pm: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2000-2002 Graham Barr . All rights reserved. 2 | # This program is free software; you can redistribute it and/or 3 | # modify it under the same terms as Perl itself. 4 | 5 | package Convert::ASN1; 6 | 7 | use 5.004; 8 | use strict; 9 | use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS @opParts @opName $AUTOLOAD); 10 | use Exporter; 11 | 12 | use constant CHECK_UTF8 => $] > 5.007; 13 | 14 | BEGIN { 15 | local $SIG{__DIE__}; 16 | eval { require bytes and 'bytes'->import }; 17 | 18 | if (CHECK_UTF8) { 19 | require Encode; 20 | require utf8; 21 | } 22 | 23 | @ISA = qw(Exporter); 24 | 25 | %EXPORT_TAGS = ( 26 | io => [qw(asn_recv asn_send asn_read asn_write asn_get asn_ready)], 27 | 28 | debug => [qw(asn_dump asn_hexdump)], 29 | 30 | const => [qw(ASN_BOOLEAN ASN_INTEGER ASN_BIT_STR ASN_OCTET_STR 31 | ASN_NULL ASN_OBJECT_ID ASN_REAL ASN_ENUMERATED 32 | ASN_SEQUENCE ASN_SET ASN_PRINT_STR ASN_IA5_STR 33 | ASN_UTC_TIME ASN_GENERAL_TIME ASN_RELATIVE_OID 34 | ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE 35 | ASN_PRIMITIVE ASN_CONSTRUCTOR ASN_LONG_LEN ASN_EXTENSION_ID ASN_BIT)], 36 | 37 | tag => [qw(asn_tag asn_decode_tag2 asn_decode_tag asn_encode_tag asn_decode_length asn_encode_length)] 38 | ); 39 | 40 | @EXPORT_OK = map { @$_ } values %EXPORT_TAGS; 41 | $EXPORT_TAGS{all} = \@EXPORT_OK; 42 | 43 | @opParts = qw( 44 | cTAG cTYPE cVAR cLOOP cOPT cEXT cCHILD cDEFINE 45 | ); 46 | 47 | @opName = qw( 48 | opUNKNOWN opBOOLEAN opINTEGER opBITSTR opSTRING opNULL opOBJID opREAL 49 | opSEQUENCE opEXPLICIT opSET opUTIME opGTIME opUTF8 opANY opCHOICE opROID opBCD 50 | opEXTENSIONS 51 | ); 52 | 53 | foreach my $l (\@opParts, \@opName) { 54 | my $i = 0; 55 | foreach my $name (@$l) { 56 | my $j = $i++; 57 | no strict 'refs'; 58 | *{__PACKAGE__ . '::' . $name} = sub () { $j } 59 | } 60 | } 61 | } 62 | 63 | sub _internal_syms { 64 | my $pkg = caller; 65 | no strict 'refs'; 66 | for my $sub (@opParts,@opName,'dump_op') { 67 | *{$pkg . '::' . $sub} = \&{__PACKAGE__ . '::' . $sub}; 68 | } 69 | } 70 | 71 | sub ASN_BOOLEAN () { 0x01 } 72 | sub ASN_INTEGER () { 0x02 } 73 | sub ASN_BIT_STR () { 0x03 } 74 | sub ASN_OCTET_STR () { 0x04 } 75 | sub ASN_NULL () { 0x05 } 76 | sub ASN_OBJECT_ID () { 0x06 } 77 | sub ASN_REAL () { 0x09 } 78 | sub ASN_ENUMERATED () { 0x0A } 79 | sub ASN_RELATIVE_OID () { 0x0D } 80 | sub ASN_SEQUENCE () { 0x10 } 81 | sub ASN_SET () { 0x11 } 82 | sub ASN_PRINT_STR () { 0x13 } 83 | sub ASN_IA5_STR () { 0x16 } 84 | sub ASN_UTC_TIME () { 0x17 } 85 | sub ASN_GENERAL_TIME () { 0x18 } 86 | 87 | sub ASN_UNIVERSAL () { 0x00 } 88 | sub ASN_APPLICATION () { 0x40 } 89 | sub ASN_CONTEXT () { 0x80 } 90 | sub ASN_PRIVATE () { 0xC0 } 91 | 92 | sub ASN_PRIMITIVE () { 0x00 } 93 | sub ASN_CONSTRUCTOR () { 0x20 } 94 | 95 | sub ASN_LONG_LEN () { 0x80 } 96 | sub ASN_EXTENSION_ID () { 0x1F } 97 | sub ASN_BIT () { 0x80 } 98 | 99 | 100 | sub new { 101 | my $pkg = shift; 102 | my $self = bless {}, $pkg; 103 | 104 | $self->configure(@_); 105 | $self; 106 | } 107 | 108 | 109 | sub configure { 110 | my $self = shift; 111 | my %opt = @_; 112 | 113 | $self->{options}{encoding} = uc($opt{encoding} || 'BER'); 114 | 115 | unless ($self->{options}{encoding} =~ /^[BD]ER$/) { 116 | require Carp; 117 | Carp::croak("Unsupported encoding format '$opt{encoding}'"); 118 | } 119 | 120 | # IMPLICIT as default for backwards compatibility, even though it's wrong. 121 | $self->{options}{tagdefault} = uc($opt{tagdefault} || 'IMPLICIT'); 122 | 123 | unless ($self->{options}{tagdefault} =~ /^(?:EXPLICIT|IMPLICIT)$/) { 124 | require Carp; 125 | Carp::croak("Default tagging must be EXPLICIT/IMPLICIT. Not $opt{tagdefault}"); 126 | } 127 | 128 | 129 | for my $type (qw(encode decode)) { 130 | if (exists $opt{$type}) { 131 | while(my($what,$value) = each %{$opt{$type}}) { 132 | $self->{options}{"${type}_${what}"} = $value; 133 | } 134 | } 135 | } 136 | } 137 | 138 | 139 | 140 | sub find { 141 | my $self = shift; 142 | my $what = shift; 143 | return unless exists $self->{tree}{$what}; 144 | my %new = %$self; 145 | $new{script} = $new{tree}->{$what}; 146 | bless \%new, ref($self); 147 | } 148 | 149 | 150 | sub prepare { 151 | my $self = shift; 152 | my $asn = shift; 153 | 154 | $self = $self->new unless ref($self); 155 | my $tree; 156 | if( ref($asn) eq 'GLOB' ){ 157 | local $/ = undef; 158 | my $txt = <$asn>; 159 | $tree = Convert::ASN1::parser::parse($txt,$self->{options}{tagdefault}); 160 | } else { 161 | $tree = Convert::ASN1::parser::parse($asn,$self->{options}{tagdefault}); 162 | } 163 | 164 | unless ($tree) { 165 | $self->{error} = $@; 166 | return; 167 | ### If $self has been set to a new object, not returning 168 | ### this object here will destroy the object, so the caller 169 | ### won't be able to get at the error. 170 | } 171 | 172 | $self->{tree} = _pack_struct($tree); 173 | $self->{script} = (values %$tree)[0]; 174 | $self; 175 | } 176 | 177 | sub prepare_file { 178 | my $self = shift; 179 | my $asnp = shift; 180 | 181 | local *ASN; 182 | open( ASN, $asnp ) 183 | or do{ $self->{error} = $@; return; }; 184 | my $ret = $self->prepare( \*ASN ); 185 | close( ASN ); 186 | $ret; 187 | } 188 | 189 | sub registeroid { 190 | my $self = shift; 191 | my $oid = shift; 192 | my $handler = shift; 193 | 194 | $self->{options}{oidtable}{$oid}=$handler; 195 | $self->{oidtable}{$oid}=$handler; 196 | } 197 | 198 | sub registertype { 199 | my $self = shift; 200 | my $def = shift; 201 | my $type = shift; 202 | my $handler = shift; 203 | 204 | $self->{options}{handlers}{$def}{$type}=$handler; 205 | } 206 | 207 | # In XS the will convert the tree between perl and C structs 208 | 209 | sub _pack_struct { $_[0] } 210 | sub _unpack_struct { $_[0] } 211 | 212 | ## 213 | ## Encoding 214 | ## 215 | 216 | sub encode { 217 | my $self = shift; 218 | my $stash = @_ == 1 ? shift : { @_ }; 219 | my $buf = ''; 220 | local $SIG{__DIE__}; 221 | eval { _encode($self->{options}, $self->{script}, $stash, [], $buf) } 222 | or do { $self->{error} = $@; undef } 223 | } 224 | 225 | 226 | 227 | # Encode tag value for encoding. 228 | # We assume that the tag has been correctly generated with asn_tag() 229 | 230 | sub asn_encode_tag { 231 | $_[0] >> 8 232 | ? $_[0] & 0x8000 233 | ? $_[0] & 0x800000 234 | ? pack("V",$_[0]) 235 | : substr(pack("V",$_[0]),0,3) 236 | : pack("v", $_[0]) 237 | : pack("C",$_[0]); 238 | } 239 | 240 | 241 | # Encode a length. If < 0x80 then encode as a byte. Otherwise encode 242 | # 0x80 | num_bytes followed by the bytes for the number. top end 243 | # bytes of all zeros are not encoded 244 | 245 | sub asn_encode_length { 246 | 247 | if($_[0] >> 7) { 248 | my $lenlen = &num_length; 249 | 250 | return pack("Ca*", $lenlen | 0x80, substr(pack("N",$_[0]), -$lenlen)); 251 | } 252 | 253 | return pack("C", $_[0]); 254 | } 255 | 256 | 257 | ## 258 | ## Decoding 259 | ## 260 | 261 | sub decode { 262 | my $self = shift; 263 | my $ret; 264 | 265 | local $SIG{__DIE__}; 266 | eval { 267 | my (%stash, $result); 268 | my $script = $self->{script}; 269 | my $stash = \$result; 270 | 271 | while ($script) { 272 | my $child = $script->[0] or last; 273 | if (@$script > 1 or defined $child->[cVAR]) { 274 | $result = $stash = \%stash; 275 | last; 276 | } 277 | last if $child->[cTYPE] == opCHOICE or $child->[cLOOP]; 278 | $script = $child->[cCHILD]; 279 | } 280 | 281 | _decode( 282 | $self->{options}, 283 | $self->{script}, 284 | $stash, 285 | 0, 286 | length $_[0], 287 | undef, 288 | {}, 289 | $_[0]); 290 | 291 | $ret = $result; 292 | 1; 293 | } or $self->{'error'} = $@ || 'Unknown error'; 294 | 295 | $ret; 296 | } 297 | 298 | 299 | sub asn_decode_length { 300 | return unless length $_[0]; 301 | 302 | my $len = unpack("C",$_[0]); 303 | 304 | if($len & 0x80) { 305 | $len &= 0x7f or return (1,-1); 306 | 307 | return if $len >= length $_[0]; 308 | 309 | return (1+$len, unpack("N", "\0" x (4 - $len) . substr($_[0],1,$len))); 310 | } 311 | return (1, $len); 312 | } 313 | 314 | 315 | sub asn_decode_tag { 316 | return unless length $_[0]; 317 | 318 | my $tag = unpack("C", $_[0]); 319 | my $n = 1; 320 | 321 | if(($tag & 0x1f) == 0x1f) { 322 | my $b; 323 | do { 324 | return if $n >= length $_[0]; 325 | $b = unpack("C",substr($_[0],$n,1)); 326 | $tag |= $b << (8 * $n++); 327 | } while($b & 0x80); 328 | } 329 | ($n, $tag); 330 | } 331 | 332 | 333 | sub asn_decode_tag2 { 334 | return unless length $_[0]; 335 | 336 | my $tag = unpack("C",$_[0]); 337 | my $num = $tag & 0x1f; 338 | my $len = 1; 339 | 340 | if($num == 0x1f) { 341 | $num = 0; 342 | my $b; 343 | do { 344 | return if $len >= length $_[0]; 345 | $b = unpack("C",substr($_[0],$len++,1)); 346 | $num = ($num << 7) + ($b & 0x7f); 347 | } while($b & 0x80); 348 | } 349 | ($len, $tag, $num); 350 | } 351 | 352 | 353 | ## 354 | ## Utilities 355 | ## 356 | 357 | # How many bytes are needed to encode a number 358 | 359 | sub num_length { 360 | $_[0] >> 8 361 | ? $_[0] >> 16 362 | ? $_[0] >> 24 363 | ? 4 364 | : 3 365 | : 2 366 | : 1 367 | } 368 | 369 | # Convert from a bigint to an octet string 370 | 371 | sub i2osp { 372 | my($num, $biclass) = @_; 373 | eval "use $biclass"; 374 | $num = $biclass->new($num); 375 | my $neg = $num < 0 376 | and $num = abs($num+1); 377 | my $base = $biclass->new(256); 378 | my $result = ''; 379 | while($num != 0) { 380 | my $r = $num % $base; 381 | $num = ($num-$r) / $base; 382 | $result .= pack("C",$r); 383 | } 384 | $result ^= pack("C",255) x length($result) if $neg; 385 | return scalar reverse $result; 386 | } 387 | 388 | # Convert from an octet string to a bigint 389 | 390 | sub os2ip { 391 | my($os, $biclass) = @_; 392 | eval "require $biclass"; 393 | my $base = $biclass->new(256); 394 | my $result = $biclass->new(0); 395 | my $neg = unpack("C",$os) >= 0x80 396 | and $os ^= pack("C",255) x length($os); 397 | for (unpack("C*",$os)) { 398 | $result = ($result * $base) + $_; 399 | } 400 | return $neg ? ($result + 1) * -1 : $result; 401 | } 402 | 403 | # Given a class and a tag, calculate an integer which when encoded 404 | # will become the tag. This means that the class bits are always 405 | # in the bottom byte, so are the tag bits if tag < 30. Otherwise 406 | # the tag is in the upper 3 bytes. The upper bytes are encoded 407 | # with bit8 representing that there is another byte. This 408 | # means the max tag we can do is 0x1fffff 409 | 410 | sub asn_tag { 411 | my($class,$value) = @_; 412 | 413 | die sprintf "Bad tag class 0x%x",$class 414 | if $class & ~0xe0; 415 | 416 | unless ($value & ~0x1f or $value == 0x1f) { 417 | return (($class & 0xe0) | $value); 418 | } 419 | 420 | die sprintf "Tag value 0x%08x too big\n",$value 421 | if $value & 0xffe00000; 422 | 423 | $class = ($class | 0x1f) & 0xff; 424 | 425 | my @t = ($value & 0x7f); 426 | unshift @t, (0x80 | ($value & 0x7f)) while $value >>= 7; 427 | unpack("V",pack("C4",$class,@t,0,0)); 428 | } 429 | 430 | 431 | BEGIN { 432 | # When we have XS &_encode will be defined by the XS code 433 | # so will all the subs in these required packages 434 | unless (defined &_encode) { 435 | require Convert::ASN1::_decode; 436 | require Convert::ASN1::_encode; 437 | require Convert::ASN1::IO; 438 | } 439 | 440 | require Convert::ASN1::parser; 441 | } 442 | 443 | sub AUTOLOAD { 444 | require Convert::ASN1::Debug if $AUTOLOAD =~ /dump/; 445 | goto &{$AUTOLOAD} if defined &{$AUTOLOAD}; 446 | require Carp; 447 | my $pkg = ref($_[0]) || ($_[0] =~ /^[\w\d]+(?:::[\w\d]+)*$/)[0]; 448 | if ($pkg and UNIVERSAL::isa($pkg, __PACKAGE__)) { # guess it was a method call 449 | $AUTOLOAD =~ s/.*:://; 450 | Carp::croak(sprintf q{Can't locate object method "%s" via package "%s"},$AUTOLOAD,$pkg); 451 | } 452 | else { 453 | Carp::croak(sprintf q{Undefined subroutine &%s called}, $AUTOLOAD); 454 | } 455 | } 456 | 457 | sub DESTROY {} 458 | 459 | sub error { $_[0]->{error} } 460 | 1; 461 | -------------------------------------------------------------------------------- /lib/Convert/ASN1.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Convert::ASN1 - ASN.1 Encode/Decode library 4 | 5 | =head1 SYNOPSIS 6 | 7 | use Convert::ASN1; 8 | 9 | $asn = Convert::ASN1->new; 10 | $asn->prepare(q< 11 | 12 | [APPLICATION 7] SEQUENCE { 13 | int INTEGER, 14 | str OCTET STRING 15 | } 16 | 17 | >); 18 | 19 | $pdu = $asn->encode( int => 7, str => "string"); 20 | 21 | $out = $asn->decode($pdu); 22 | print $out->{int}," ",$out->{str},"\n"; 23 | 24 | use Convert::ASN1 qw(:io); 25 | 26 | $peer = asn_recv($sock,$buffer,0); 27 | $nbytes = asn_read($fh, $buffer); 28 | $nbytes = asn_send($sock, $buffer, $peer); 29 | $nbytes = asn_send($sock, $buffer); 30 | $nbytes = asn_write($fh, $buffer); 31 | $buffer = asn_get($fh); 32 | $yes = asn_ready($fh) 33 | 34 | =head1 DESCRIPTION 35 | 36 | Convert::ASN1 encodes and decodes ASN.1 data structures using BER/DER 37 | rules. 38 | 39 | =head1 METHODS 40 | 41 | =head2 new ( [OPTIONS] ) 42 | 43 | Constructor, creates a new object. 44 | 45 | If given, B are the same ones as for L below. 46 | 47 | =head2 error () 48 | 49 | Returns the last error. 50 | 51 | =head2 configure ( OPTIONS ) 52 | 53 | Configure options to control how Convert::ASN1 will perform various tasks. 54 | Options are passed as name-value pairs. 55 | 56 | =over 4 57 | 58 | =item encode 59 | 60 | Reference to a hash which contains various encode options. 61 | 62 | =item decode 63 | 64 | Reference to a hash which contains various decode options. 65 | 66 | =item encoding 67 | 68 | One of 'BER' or 'DER'. The default is 'BER' 69 | 70 | =item tagdefault 71 | 72 | One of 'EXPLICIT' or 'IMPLICIT'. 73 | Default tagging conventions are normally given in the ASN.1 module definition (not supported by the parser). The ASN.1 spec states EXPLICIT tagging is the default, but this option has IMPLICIT tagging default for backward compatibility reasons. 74 | 75 | =back 76 | 77 | Encode options 78 | 79 | =over 4 80 | 81 | =item real 82 | 83 | Which encoding to use for real's. One of 'binary', 'nr1', 'nr2', 'nr3' 84 | 85 | =item time 86 | 87 | This controls how UTCTime and GeneralizedTime elements are encoded. The default 88 | is C. 89 | 90 | =over 4 91 | 92 | =item utctime 93 | 94 | The value passed will be encoded without a zone, ie a UTC value. 95 | 96 | =item withzone 97 | 98 | The value will be encoded with a zone. By default it will be encoded 99 | using the local time offset. The offset may be set using the C 100 | configure option. 101 | 102 | =item raw 103 | 104 | The value passed should already be in the correct format and will be copied 105 | into the PDU as-is. 106 | 107 | =back 108 | 109 | =item timezone 110 | 111 | By default UTCTime and GeneralizedTime will be encoded using the local 112 | time offset from UTC. This will over-ride that. It is an offset from UTC 113 | in seconds. This option can be overridden by passing a reference to a 114 | list of two values as the time value. The list should contain the time 115 | value and the offset from UTC in seconds. 116 | 117 | =item bigint 118 | 119 | If during encoding an value greater than 32 bits is discovered and 120 | is not already a big integer object, then the value will first be 121 | converted into a big integer object. This option controls the big 122 | integer class into which the objects will be blessed. The default 123 | is to use Math::BigInt 124 | 125 | =back 126 | 127 | Decode options 128 | 129 | =over 4 130 | 131 | =item time 132 | 133 | This controls how a UTCTime or a GeneralizedTime element will be decoded. The default 134 | is C. 135 | 136 | =over 4 137 | 138 | =item utctime 139 | 140 | The value returned will be a time value as returned by the C