From 3b8ddfe2f303b8931842e52bdb5c07c97042f651 Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Sun, 10 Nov 2019 04:30:14 +0300 Subject: [PATCH] tcl_tests: Add TCL tests files All source files are converted from cp1251 to utf-8. --- tcl_tests/097.ciphers | 11 + tcl_tests/098.ciphers | 11 + tcl_tests/_exists | 0 tcl_tests/aes0.enc | Bin 0 -> 48 bytes tcl_tests/aes1.enc | Bin 0 -> 32 bytes tcl_tests/apache.try | 33 + tcl_tests/asn.tcl | 1419 +++++++++++++++++ tcl_tests/base64.tcl | 326 ++++ tcl_tests/ca.try | 125 ++ tcl_tests/calchash.tcl | 48 + tcl_tests/calcstat | 60 + tcl_tests/cbc0.enc | 2 + tcl_tests/cbc1.enc | 2 + tcl_tests/cfb0.enc | 2 + tcl_tests/cfb1.enc | 2 + tcl_tests/ciphers.try | 38 + tcl_tests/client.try | 143 ++ tcl_tests/cms.try | 173 ++ tcl_tests/cms2.try | 207 +++ tcl_tests/cms_cs.try | 126 ++ tcl_tests/cms_io.try | 81 + tcl_tests/cmsenc.try | 190 +++ tcl_tests/cmsenc_cs.try | 215 +++ tcl_tests/cmsenc_io.try | 108 ++ tcl_tests/cmsenc_sc.try | 191 +++ tcl_tests/cmstc262019.try | 34 + tcl_tests/cnt0.enc | 1 + tcl_tests/cnt1.enc | 1 + tcl_tests/cp10.ciphers | 22 + tcl_tests/cp20.ciphers | 8 + tcl_tests/cp21.ciphers | 17 + tcl_tests/csp3.ciphers | 4 + tcl_tests/csp36.ciphers | 4 + tcl_tests/csp36r2.ciphers | 4 + tcl_tests/csp36r3.ciphers | 4 + tcl_tests/csp36r4.ciphers | 4 + tcl_tests/csp39.ciphers | 4 + tcl_tests/csp4.ciphers | 10 + tcl_tests/csp4r2.ciphers | 10 + tcl_tests/csp4r3.ciphers | 14 + tcl_tests/csp5.ciphers | 18 + tcl_tests/dgst.try | 126 ++ tcl_tests/dgst_CF.dat | 1 + tcl_tests/dgst_ex1.dat | 1 + tcl_tests/dgst_ex2.dat | 1 + tcl_tests/enc.try | 222 +++ tcl_tests/engine.try | 33 + tcl_tests/enums.tcl | 23 + tcl_tests/getengine.tcl | 37 + tcl_tests/http.tcl | 28 + tcl_tests/hwkeys.tcl | 229 +++ tcl_tests/interop.try | 156 ++ tcl_tests/kbstrike.exe | Bin 0 -> 64512 bytes tcl_tests/key.pem | 5 + tcl_tests/mac-grasshopper.dat | Bin 0 -> 64 bytes tcl_tests/mac-magma.dat | 2 + tcl_tests/mac.try | 121 ++ tcl_tests/macpkm1.enc | Bin 0 -> 1032 bytes tcl_tests/magma1.enc | 1 + tcl_tests/magma_acpkm_plain.enc | Bin 0 -> 1032 bytes tcl_tests/magma_enc | 1 + tcl_tests/magma_plain | 2 + tcl_tests/magma_plain.enc | 2 + tcl_tests/make_other.sh | 25 + tcl_tests/mkn2o.tcl | 28 + tcl_tests/mkoidf.tcl | 5 + tcl_tests/name2oid.tcl | 44 + tcl_tests/name2oid.tst | 0 tcl_tests/nopath.try | 68 + tcl_tests/ocsp.try | 153 ++ tcl_tests/oidfile | 43 + tcl_tests/opnssl.sh | 11 + tcl_tests/ossltest.tcl | 1052 ++++++++++++ tcl_tests/pkcs12.try | 55 + tcl_tests/pkcs7.tcl | 187 +++ tcl_tests/pkcs8.try | 170 ++ tcl_tests/pkgIndex.tcl | 6 + tcl_tests/plain.enc | 1 + tcl_tests/private/gost2001_A.pem | 5 + tcl_tests/private/gost2001_B.pem | 5 + tcl_tests/private/gost2001_C.pem | 5 + tcl_tests/private/gost2001_XA.pem | 5 + tcl_tests/private/gost2001_XB.pem | 5 + tcl_tests/private/gost2012_256_A.pem | 5 + tcl_tests/private/gost2012_256_B.pem | 5 + tcl_tests/private/gost2012_256_C.pem | 5 + tcl_tests/private/gost2012_256_XA.pem | 5 + tcl_tests/private/gost2012_256_XB.pem | 5 + tcl_tests/private/gost2012_512_A.pem | 6 + tcl_tests/private/gost2012_512_B.pem | 6 + tcl_tests/private/rsa_1024.pem | 16 + tcl_tests/req-genpkey.try | 83 + tcl_tests/req-newkey.try | 95 ++ tcl_tests/runtest.bat | 88 + tcl_tests/runtest.sh | 155 ++ tcl_tests/runtest1.bat | 81 + tcl_tests/runtest2.bat | 81 + tcl_tests/server.try | 205 +++ tcl_tests/smime.try | 173 ++ tcl_tests/smime2.try | 205 +++ tcl_tests/smime_cs.try | 83 + tcl_tests/smime_io.try | 80 + tcl_tests/smimeenc.try | 185 +++ tcl_tests/smimeenc_io.try | 104 ++ tcl_tests/ssl.try | 322 ++++ .../tc26_cms/encrypted_keyagree_a211.pem | 14 + .../tc26_cms/encrypted_keyagree_a221.pem | 10 + .../tc26_cms/encrypted_keytrans_a231.pem | 11 + .../tc26_cms/encrypted_keytrans_a241.pem | 13 + .../tc26_cms/encrypted_kuznyechik_a421.pem | 5 + tcl_tests/tc26_cms/encrypted_magma_a411.pem | 5 + tcl_tests/tc26_cms/encryption_key.hex | 1 + tcl_tests/tc26_cms/hashed_a311.pem | 5 + tcl_tests/tc26_cms/hashed_a321.pem | 6 + tcl_tests/tc26_cms/recipient256_cert.pem | 13 + tcl_tests/tc26_cms/recipient256_key.pem | 4 + tcl_tests/tc26_cms/recipient512_cert.pem | 14 + tcl_tests/tc26_cms/recipient512_key.pem | 5 + tcl_tests/tc26_cms/root256_cert.pem | 13 + tcl_tests/tc26_cms/root256_key.pem | 4 + tcl_tests/tc26_cms/sender256_cert.pem | 13 + tcl_tests/tc26_cms/sender256_key.pem | 4 + tcl_tests/tc26_cms/sender512_cert.pem | 14 + tcl_tests/tc26_cms/sender512_key.pem | 5 + tcl_tests/tc26_cms/signed_a111.pem | 25 + tcl_tests/tc26_cms/signed_a121.pem | 19 + tcl_tests/test.tcl | 452 ++++++ tcl_tests/tmpl.try | 6 + tcl_tests/ts.try | 305 ++++ tcl_tests/vn4.ciphers | 15 + tcl_tests/wcli.try | 150 ++ tcl_tests/yarrowc.tcl | 70 + 132 files changed, 9730 insertions(+) create mode 100644 tcl_tests/097.ciphers create mode 100644 tcl_tests/098.ciphers create mode 100644 tcl_tests/_exists create mode 100644 tcl_tests/aes0.enc create mode 100644 tcl_tests/aes1.enc create mode 100644 tcl_tests/apache.try create mode 100644 tcl_tests/asn.tcl create mode 100644 tcl_tests/base64.tcl create mode 100644 tcl_tests/ca.try create mode 100755 tcl_tests/calchash.tcl create mode 100644 tcl_tests/calcstat create mode 100644 tcl_tests/cbc0.enc create mode 100644 tcl_tests/cbc1.enc create mode 100644 tcl_tests/cfb0.enc create mode 100644 tcl_tests/cfb1.enc create mode 100644 tcl_tests/ciphers.try create mode 100644 tcl_tests/client.try create mode 100644 tcl_tests/cms.try create mode 100644 tcl_tests/cms2.try create mode 100644 tcl_tests/cms_cs.try create mode 100644 tcl_tests/cms_io.try create mode 100644 tcl_tests/cmsenc.try create mode 100644 tcl_tests/cmsenc_cs.try create mode 100644 tcl_tests/cmsenc_io.try create mode 100644 tcl_tests/cmsenc_sc.try create mode 100644 tcl_tests/cmstc262019.try create mode 100644 tcl_tests/cnt0.enc create mode 100644 tcl_tests/cnt1.enc create mode 100644 tcl_tests/cp10.ciphers create mode 100644 tcl_tests/cp20.ciphers create mode 100644 tcl_tests/cp21.ciphers create mode 100644 tcl_tests/csp3.ciphers create mode 100644 tcl_tests/csp36.ciphers create mode 100644 tcl_tests/csp36r2.ciphers create mode 100644 tcl_tests/csp36r3.ciphers create mode 100644 tcl_tests/csp36r4.ciphers create mode 100644 tcl_tests/csp39.ciphers create mode 100644 tcl_tests/csp4.ciphers create mode 100644 tcl_tests/csp4r2.ciphers create mode 100644 tcl_tests/csp4r3.ciphers create mode 100644 tcl_tests/csp5.ciphers create mode 100644 tcl_tests/dgst.try create mode 100644 tcl_tests/dgst_CF.dat create mode 100644 tcl_tests/dgst_ex1.dat create mode 100644 tcl_tests/dgst_ex2.dat create mode 100644 tcl_tests/enc.try create mode 100644 tcl_tests/engine.try create mode 100644 tcl_tests/enums.tcl create mode 100644 tcl_tests/getengine.tcl create mode 100644 tcl_tests/http.tcl create mode 100644 tcl_tests/hwkeys.tcl create mode 100644 tcl_tests/interop.try create mode 100755 tcl_tests/kbstrike.exe create mode 100644 tcl_tests/key.pem create mode 100644 tcl_tests/mac-grasshopper.dat create mode 100644 tcl_tests/mac-magma.dat create mode 100644 tcl_tests/mac.try create mode 100644 tcl_tests/macpkm1.enc create mode 100644 tcl_tests/magma1.enc create mode 100644 tcl_tests/magma_acpkm_plain.enc create mode 100644 tcl_tests/magma_enc create mode 100644 tcl_tests/magma_plain create mode 100644 tcl_tests/magma_plain.enc create mode 100755 tcl_tests/make_other.sh create mode 100644 tcl_tests/mkn2o.tcl create mode 100644 tcl_tests/mkoidf.tcl create mode 100644 tcl_tests/name2oid.tcl create mode 100644 tcl_tests/name2oid.tst create mode 100644 tcl_tests/nopath.try create mode 100644 tcl_tests/ocsp.try create mode 100644 tcl_tests/oidfile create mode 100755 tcl_tests/opnssl.sh create mode 100644 tcl_tests/ossltest.tcl create mode 100644 tcl_tests/pkcs12.try create mode 100644 tcl_tests/pkcs7.tcl create mode 100644 tcl_tests/pkcs8.try create mode 100644 tcl_tests/pkgIndex.tcl create mode 100644 tcl_tests/plain.enc create mode 100644 tcl_tests/private/gost2001_A.pem create mode 100644 tcl_tests/private/gost2001_B.pem create mode 100644 tcl_tests/private/gost2001_C.pem create mode 100644 tcl_tests/private/gost2001_XA.pem create mode 100644 tcl_tests/private/gost2001_XB.pem create mode 100644 tcl_tests/private/gost2012_256_A.pem create mode 100644 tcl_tests/private/gost2012_256_B.pem create mode 100644 tcl_tests/private/gost2012_256_C.pem create mode 100644 tcl_tests/private/gost2012_256_XA.pem create mode 100644 tcl_tests/private/gost2012_256_XB.pem create mode 100644 tcl_tests/private/gost2012_512_A.pem create mode 100644 tcl_tests/private/gost2012_512_B.pem create mode 100644 tcl_tests/private/rsa_1024.pem create mode 100644 tcl_tests/req-genpkey.try create mode 100644 tcl_tests/req-newkey.try create mode 100755 tcl_tests/runtest.bat create mode 100644 tcl_tests/runtest.sh create mode 100755 tcl_tests/runtest1.bat create mode 100755 tcl_tests/runtest2.bat create mode 100644 tcl_tests/server.try create mode 100644 tcl_tests/smime.try create mode 100644 tcl_tests/smime2.try create mode 100644 tcl_tests/smime_cs.try create mode 100644 tcl_tests/smime_io.try create mode 100644 tcl_tests/smimeenc.try create mode 100644 tcl_tests/smimeenc_io.try create mode 100644 tcl_tests/ssl.try create mode 100644 tcl_tests/tc26_cms/encrypted_keyagree_a211.pem create mode 100644 tcl_tests/tc26_cms/encrypted_keyagree_a221.pem create mode 100644 tcl_tests/tc26_cms/encrypted_keytrans_a231.pem create mode 100644 tcl_tests/tc26_cms/encrypted_keytrans_a241.pem create mode 100644 tcl_tests/tc26_cms/encrypted_kuznyechik_a421.pem create mode 100644 tcl_tests/tc26_cms/encrypted_magma_a411.pem create mode 100644 tcl_tests/tc26_cms/encryption_key.hex create mode 100644 tcl_tests/tc26_cms/hashed_a311.pem create mode 100644 tcl_tests/tc26_cms/hashed_a321.pem create mode 100644 tcl_tests/tc26_cms/recipient256_cert.pem create mode 100644 tcl_tests/tc26_cms/recipient256_key.pem create mode 100644 tcl_tests/tc26_cms/recipient512_cert.pem create mode 100644 tcl_tests/tc26_cms/recipient512_key.pem create mode 100644 tcl_tests/tc26_cms/root256_cert.pem create mode 100644 tcl_tests/tc26_cms/root256_key.pem create mode 100644 tcl_tests/tc26_cms/sender256_cert.pem create mode 100644 tcl_tests/tc26_cms/sender256_key.pem create mode 100644 tcl_tests/tc26_cms/sender512_cert.pem create mode 100644 tcl_tests/tc26_cms/sender512_key.pem create mode 100644 tcl_tests/tc26_cms/signed_a111.pem create mode 100644 tcl_tests/tc26_cms/signed_a121.pem create mode 100644 tcl_tests/test.tcl create mode 100644 tcl_tests/tmpl.try create mode 100644 tcl_tests/ts.try create mode 100644 tcl_tests/vn4.ciphers create mode 100644 tcl_tests/wcli.try create mode 100644 tcl_tests/yarrowc.tcl diff --git a/tcl_tests/097.ciphers b/tcl_tests/097.ciphers new file mode 100644 index 0000000..1a33a23 --- /dev/null +++ b/tcl_tests/097.ciphers @@ -0,0 +1,11 @@ +rsa:1024 { + DHE-RSA-AES256-SHA tls-ref-097.lan.cryptocom.ru:443 + SSLv2:DES-CBC3-MD5 tls-ref-097.lan.cryptocom.ru:4402 + SSLv3:DES-CBC-SHA tls-ref-097.lan.cryptocom.ru:4403 + RC4-SHA tls-ref-097.lan.cryptocom.ru:4404 +} + +dsa:dsaparams.pem { + DHE-DSS-AES256-SHA tls-ref-097.lan.cryptocom.ru:444 + SSLv3:EDH-DSS-DES-CBC3-SHA tls-ref-097.lan.cryptocom.ru:4401 +} diff --git a/tcl_tests/098.ciphers b/tcl_tests/098.ciphers new file mode 100644 index 0000000..cbe8cf0 --- /dev/null +++ b/tcl_tests/098.ciphers @@ -0,0 +1,11 @@ +rsa:1024 { + DHE-RSA-AES256-SHA tls-ref-098.lan.cryptocom.ru:443 + SSLv2:DES-CBC3-MD5 tls-ref-098.lan.cryptocom.ru:4402 + SSLv3:DES-CBC-SHA tls-ref-098.lan.cryptocom.ru:4403 + RC4-SHA tls-ref-098.lan.cryptocom.ru:4404 +} + +dsa:dsaparams.pem { + DHE-DSS-AES256-SHA tls-ref-098.lan.cryptocom.ru:444 + SSLv3:EDH-DSS-DES-CBC3-SHA tls-ref-098.lan.cryptocom.ru:4401 +} diff --git a/tcl_tests/_exists b/tcl_tests/_exists new file mode 100644 index 0000000..e69de29 diff --git a/tcl_tests/aes0.enc b/tcl_tests/aes0.enc new file mode 100644 index 0000000000000000000000000000000000000000..372afb6c127ed402fd55d78755a92ca9a8af9d92 GIT binary patch literal 48 zcmV-00MGwZVQh3|WM5xM^%JA&XZhdAduLWpVz?;DrCBlE0Oha-0K5zOM*i5POM1df G6-kIcuorUx literal 0 HcmV?d00001 diff --git a/tcl_tests/aes1.enc b/tcl_tests/aes1.enc new file mode 100644 index 0000000000000000000000000000000000000000..b9e00dcda8b293eaf2460f54d1b8195e97cafc9f GIT binary patch literal 32 ocmX?6ogU_&v_s?M(n!O*3{N+(G3?>~<@WE!Qtzrm-lCoz0R5m3JOBUy literal 0 HcmV?d00001 diff --git a/tcl_tests/apache.try b/tcl_tests/apache.try new file mode 100644 index 0000000..b126970 --- /dev/null +++ b/tcl_tests/apache.try @@ -0,0 +1,33 @@ +#!/usr/bin/tclsh +lappend auto_path [file dirname [info script]] +package require ossltest + +set test_dir [file normalize [file dirname [info script]]] + +cd $::test::dir +start_tests "Тесты на API, используемый только в Apache" + + + +makeCA + +if {$tcl_platform(platform) eq "windows"} { + # Add openssl dir to PATH + set dlldir [file nativename [file normalize $test_dir/../../openssl]] + set env(PATH) "$dlldir;$env(PATH)" +} +test "EVP_PKEY_copy_parameters gost94" { + makeRegisteredUser "U_apache_94" gost94:A + exec $test_dir/copy_param U_apache_94/seckey.pem U_apache_94/cert.pem +} 0 "EVP_PKEY_missing_parameters before copy: 1 +EVP_PKEY_missing_parameters after copy: 0 +Check private key:Ok" + +test "EVP_PKEY_copy_parameters gost2001" { + makeRegisteredUser "U_apache_94" gost2001:A + exec $test_dir/copy_param U_apache_94/seckey.pem U_apache_94/cert.pem +} 0 "EVP_PKEY_missing_parameters before copy: 1 +EVP_PKEY_missing_parameters after copy: 0 +Check private key:Ok" + +end_tests diff --git a/tcl_tests/asn.tcl b/tcl_tests/asn.tcl new file mode 100644 index 0000000..d9a2581 --- /dev/null +++ b/tcl_tests/asn.tcl @@ -0,0 +1,1419 @@ +#----------------------------------------------------------------------------- +# Copyright (C) 1999-2004 Jochen C. Loewer (loewerj@web.de) +# Copyright (C) 2004-2006 Michael Schlenker (mic42@users.sourceforge.net) +#----------------------------------------------------------------------------- +# +# A partial ASN decoder/encoder implementation in plain Tcl. +# +# See ASN.1 (X.680) and BER (X.690). +# See 'asn_ber_intro.txt' in this directory. +# +# This software is copyrighted by Jochen C. Loewer (loewerj@web.de). The +# following terms apply to all files associated with the software unless +# explicitly disclaimed in individual files. +# +# The authors hereby grant permission to use, copy, modify, distribute, +# and license this software and its documentation for any purpose, provided +# that existing copyright notices are retained in all copies and that this +# notice is included verbatim in any distributions. No written agreement, +# license, or royalty fee is required for any of the authorized uses. +# Modifications to this software may be copyrighted by their authors +# and need not follow the licensing terms described here, provided that +# the new terms are clearly indicated on the first page of each file where +# they apply. +# +# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +# MODIFICATIONS. +# +# written by Jochen Loewer +# 3 June, 1999 +# +# $Id: asn.tcl,v 1.1 2012-04-04 10:50:38 igus Exp $ +# +#----------------------------------------------------------------------------- + +# needed for using wide() +package require Tcl 8.4 + +namespace eval asn { + # Encoder commands + namespace export \ + asnSequence \ + asnSequenceFromList \ + asnSet \ + asnSetFromList \ + asnApplicationConstr \ + asnApplication \ + asnContext\ + asnContextConstr\ + asnChoice \ + asnChoiceConstr \ + asnInteger \ + asnEnumeration \ + asnBoolean \ + asnOctetString \ + asnUTCTime \ + asnNumericString \ + asnPrintableString \ + asnIA5String\ + asnBMPString\ + asnUTF8String\ + asnBitString \ + asnObjectIdentifer + + # Decoder commands + namespace export \ + asnGetResponse \ + asnGetInteger \ + asnGetEnumeration \ + asnGetOctetString \ + asnGetSequence \ + asnGetSet \ + asnGetApplication \ + asnGetNumericString \ + asnGetPrintableString \ + asnGetIA5String \ + asnGetBMPString \ + asnGetUTF8String \ + asnGetObjectIdentifier \ + asnGetBoolean \ + asnGetUTCTime \ + asnGetBitString \ + asnGetContext + + # general BER utility commands + namespace export \ + asnPeekByte \ + asnGetLength \ + asnRetag + +} + +#----------------------------------------------------------------------------- +# Implementation notes: +# +# See the 'asn_ber_intro.txt' in this directory for an introduction +# into BER/DER encoding of ASN.1 information. Bibliography information +# +# A Layman's Guide to a Subset of ASN.1, BER, and DER +# +# An RSA Laboratories Technical Note +# Burton S. Kaliski Jr. +# Revised November 1, 1993 +# +# Supersedes June 3, 1991 version, which was also published as +# NIST/OSI Implementors' Workshop document SEC-SIG-91-17. +# PKCS documents are available by electronic mail to +# . +# +# Copyright (C) 1991-1993 RSA Laboratories, a division of RSA +# Data Security, Inc. License to copy this document is granted +# provided that it is identified as "RSA Data Security, Inc. +# Public-Key Cryptography Standards (PKCS)" in all material +# mentioning or referencing this document. +# 003-903015-110-000-000 +# +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# asnLength : Encode some length data. Helper command. +#----------------------------------------------------------------------------- + +proc ::asn::asnLength {len} { + + if {$len < 0} { + return -code error "Negative length octet requested" + } + if {$len < 128} { + # short form: ISO X.690 8.1.3.4 + return [binary format c $len] + } + # long form: ISO X.690 8.1.3.5 + # try to use a minimal encoding, + # even if not required by BER, but it is required by DER + # take care for signed vs. unsigned issues + if {$len < 256 } { + return [binary format H2c 81 [expr {$len - 256}]] + } + if {$len < 32769} { + # two octet signed value + return [binary format H2S 82 $len] + } + if {$len < 65536} { + return [binary format H2S 82 [expr {$len - 65536}]] + } + if {$len < 8388608} { + # three octet signed value + return [binary format H2cS 83 [expr {$len >> 16}] [expr {($len & 0xFFFF) - 65536}]] + } + if {$len < 16777216} { + # three octet signed value + return [binary format H2cS 83 [expr {($len >> 16) -256}] [expr {($len & 0xFFFF) -65536}]] + } + if {$len < 2147483649} { + # four octet signed value + return [binary format H2I 84 $len] + } + if {$len < 4294967296} { + # four octet unsigned value + return [binary format H2I 84 [expr {$len - 4294967296}]] + } + if {$len < 1099511627776} { + # five octet unsigned value + return [binary format H2 85][string range [binary format W $len] 3 end] + } + if {$len < 281474976710656} { + # six octet unsigned value + return [binary format H2 86][string range [binary format W $len] 2 end] + } + if {$len < 72057594037927936} { + # seven octet value + return [binary format H2 87][string range [binary format W $len] 1 end] + } + + # must be a 64-bit wide signed value + return [binary format H2W 88 $len] +} + +#----------------------------------------------------------------------------- +# asnSequence : Assumes that the arguments are already ASN encoded. +#----------------------------------------------------------------------------- + +proc ::asn::asnSequence {args} { + asnSequenceFromList $args +} + +proc ::asn::asnSequenceFromList {lst} { + # The sequence tag is 0x30. The length is arbitrary and thus full + # length coding is required. The arguments have to be BER encoded + # already. Constructed value, definite-length encoding. + + set out "" + foreach part $lst { + append out $part + } + set len [string length $out] + return [binary format H2a*a$len 30 [asnLength $len] $out] +} + + +#----------------------------------------------------------------------------- +# asnSet : Assumes that the arguments are already ASN encoded. +#----------------------------------------------------------------------------- + +proc ::asn::asnSet {args} { + asnSetFromList $args +} + +proc ::asn::asnSetFromList {lst} { + # The set tag is 0x31. The length is arbitrary and thus full + # length coding is required. The arguments have to be BER encoded + # already. + + set out "" + foreach part $lst { + append out $part + } + set len [string length $out] + return [binary format H2a*a$len 31 [asnLength $len] $out] +} + + +#----------------------------------------------------------------------------- +# asnApplicationConstr +#----------------------------------------------------------------------------- + +proc ::asn::asnApplicationConstr {appNumber args} { + # Packs the arguments into a constructed value with application tag. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x060 + $appNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnApplication +#----------------------------------------------------------------------------- + +proc ::asn::asnApplication {appNumber data} { + # Packs the arguments into a constructed value with application tag. + + set code [expr {0x040 + $appNumber}] + set len [string length $data] + return [binary format ca*a$len $code [asnLength $len] $data] +} + +#----------------------------------------------------------------------------- +# asnContextConstr +#----------------------------------------------------------------------------- + +proc ::asn::asnContextConstr {contextNumber args} { + # Packs the arguments into a constructed value with application tag. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x0A0 + $contextNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnContext +#----------------------------------------------------------------------------- + +proc ::asn::asnContext {contextNumber data} { + # Packs the arguments into a constructed value with application tag. + set code [expr {0x080 + $contextNumber}] + set len [string length $data] + return [binary format ca*a$len $code [asnLength $len] $data] +} +#----------------------------------------------------------------------------- +# asnChoice +#----------------------------------------------------------------------------- + +proc ::asn::asnChoice {appNumber args} { + # Packs the arguments into a choice construction. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x080 + $appNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnChoiceConstr +#----------------------------------------------------------------------------- + +proc ::asn::asnChoiceConstr {appNumber args} { + # Packs the arguments into a choice construction. + + set out "" + foreach part $args { + append out $part + } + set code [expr {0x0A0 + $appNumber}] + set len [string length $out] + return [binary format ca*a$len $code [asnLength $len] $out] +} + +#----------------------------------------------------------------------------- +# asnInteger : Encode integer value. +#----------------------------------------------------------------------------- + +proc ::asn::asnInteger {number} { + asnIntegerOrEnum 02 $number +} + +#----------------------------------------------------------------------------- +# asnEnumeration : Encode enumeration value. +#----------------------------------------------------------------------------- + +proc ::asn::asnEnumeration {number} { + asnIntegerOrEnum 0a $number +} + +#----------------------------------------------------------------------------- +# asnIntegerOrEnum : Common code for Integers and Enumerations +# No Bignum version, as we do not expect large Enums. +#----------------------------------------------------------------------------- + +proc ::asn::asnIntegerOrEnum {tag number} { + # The integer tag is 0x02 , the Enum Tag 0x0a otherwise identical. + # The length is 1, 2, 3, or 4, coded in a + # single byte. This can be done directly, no need to go through + # asnLength. The value itself is written in big-endian. + + # Known bug/issue: The command cannot handle very wide integers, i.e. + # anything above 8 bytes length. Use asnBignumInteger for those. + + # check if we really have an int + set num $number + incr num + + if {($number >= -128) && ($number < 128)} { + return [binary format H2H2c $tag 01 $number] + } + if {($number >= -32768) && ($number < 32768)} { + return [binary format H2H2S $tag 02 $number] + } + if {($number >= -8388608) && ($number < 8388608)} { + set numberb [expr {$number & 0xFFFF}] + set numbera [expr {($number >> 16) & 0xFF}] + return [binary format H2H2cS $tag 03 $numbera $numberb] + } + if {($number >= -2147483648) && ($number < 2147483648)} { + return [binary format H2H2I $tag 04 $number] + } + if {($number >= -549755813888) && ($number < 549755813888)} { + set numberb [expr {$number & 0xFFFFFFFF}] + set numbera [expr {($number >> 32) & 0xFF}] + return [binary format H2H2cI $tag 05 $numbera $numberb] + } + if {($number >= -140737488355328) && ($number < 140737488355328)} { + set numberb [expr {$number & 0xFFFFFFFF}] + set numbera [expr {($number >> 32) & 0xFFFF}] + return [binary format H2H2SI $tag 06 $numbera $numberb] + } + if {($number >= -36028797018963968) && ($number < 36028797018963968)} { + set numberc [expr {$number & 0xFFFFFFFF}] + set numberb [expr {($number >> 32) & 0xFFFF}] + set numbera [expr {($number >> 48) & 0xFF}] + return [binary format H2H2cSI $tag 07 $numbera $numberb $numberc] + } + if {($number >= -9223372036854775808) && ($number <= 9223372036854775807)} { + return [binary format H2H2W $tag 08 $number] + } + return -code error "Integer value to large to encode, use asnBigInteger" +} + +#----------------------------------------------------------------------------- +# asnBigInteger : Encode a long integer value using math::bignum +#----------------------------------------------------------------------------- + +proc ::asn::asnBigInteger {bignum} { + # require math::bignum only if it is used + package require math::bignum + + # this is a hack to check for bignum... + if {[llength $bignum] < 2 || ([lindex $bignum 0] ne "bignum")} { + return -code error "expected math::bignum value got \"$bignum\"" + } + if {[math::bignum::sign $bignum]} { + # generate two's complement form + set bits [math::bignum::bits $bignum] + set padding [expr {$bits % 8}] + set len [expr {int(ceil($bits / 8.0))}] + if {$padding == 0} { + # we need a complete extra byte for the sign + # unless this is a base 2 multiple + set test [math::bignum::fromstr 0] + math::bignum::setbit test [expr {$bits-1}] + if {[math::bignum::ne [math::bignum::abs $bignum] $test]} { + incr len + } + } + set exp [math::bignum::pow \ + [math::bignum::fromstr 256] \ + [math::bignum::fromstr $len]] + set bignum [math::bignum::add $bignum $exp] + set hex [math::bignum::tostr $bignum 16] + } else { + set bits [math::bignum::bits $bignum] + if {($bits % 8) == 0 && $bits > 0} { + set pad "00" + } else { + set pad "" + } + set hex $pad[math::bignum::tostr $bignum 16] + } + if {[string length $hex]%2} { + set hex "0$hex" + } + set octets [expr {(([string length $hex]+1)/2)}] + return [binary format H2a*H* 02 [asnLength $octets] $hex] +} + + +#----------------------------------------------------------------------------- +# asnBoolean : Encode a boolean value. +#----------------------------------------------------------------------------- + +proc ::asn::asnBoolean {bool} { + # The boolean tag is 0x01. The length is always 1, coded in + # a single byte. This can be done directly, no need to go through + # asnLength. The value itself is written in big-endian. + + return [binary format H2H2c 01 01 [expr {$bool ? 0x0FF : 0x0}]] +} + +#----------------------------------------------------------------------------- +# asnOctetString : Encode a string of arbitrary bytes +#----------------------------------------------------------------------------- + +proc ::asn::asnOctetString {string} { + # The octet tag is 0x04. The length is arbitrary, so we need + # 'asnLength' for full coding of the length. + + set len [string length $string] + return [binary format H2a*a$len 04 [asnLength $len] $string] +} + +#----------------------------------------------------------------------------- +# asnNull : Encode a null value +#----------------------------------------------------------------------------- + +proc ::asn::asnNull {} { + # Null has only one valid encoding + return \x05\x00 +} + +#----------------------------------------------------------------------------- +# asnBitstring : Encode a Bit String value +#----------------------------------------------------------------------------- + +proc ::asn::asnBitString {bitstring} { + # The bit string tag is 0x03. + # Bit strings can be either simple or constructed + # we always use simple encoding + + set bitlen [string length $bitstring] + set padding [expr {(8 - ($bitlen % 8)) % 8}] + set len [expr {($bitlen / 8) + 1}] + if {$padding != 0} {incr len} + + return [binary format H2a*B* 03 [asnLength $len] $bitstring] +} + +#----------------------------------------------------------------------------- +# asnUTCTime : Encode an UTC time string +#----------------------------------------------------------------------------- + +proc ::asn::asnUTCTime {UTCtimestring} { + # the utc time tag is 0x17. + # + # BUG: we do not check the string for well formedness + + set ascii [encoding convertto ascii $UTCtimestring] + set len [string length $ascii] + return [binary format H2a*a* 17 [asnLength $len] $ascii] +} + +#----------------------------------------------------------------------------- +# asnPrintableString : Encode a printable string +#----------------------------------------------------------------------------- +namespace eval asn { + variable nonPrintableChars {[^ A-Za-z0-9'()+,.:/?=-]} +} +proc ::asn::asnPrintableString {string} { + # the printable string tag is 0x13 + variable nonPrintableChars + # it is basically a restricted ascii string + if {[regexp $nonPrintableChars $string ]} { + return -code error "Illegal character in PrintableString." + } + + # check characters + set ascii [encoding convertto ascii $string] + return [asnEncodeString 13 $ascii] +} + +#----------------------------------------------------------------------------- +# asnIA5String : Encode an Ascii String +#----------------------------------------------------------------------------- +proc ::asn::asnIA5String {string} { + # the IA5 string tag is 0x16 + # check for extended charachers + if {[string length $string]!=[string bytelength $string]} { + return -code error "Illegal character in IA5String" + } + set ascii [encoding convertto ascii $string] + return [asnEncodeString 16 $ascii] +} + +#----------------------------------------------------------------------------- +# asnNumericString : Encode a Numeric String type +#----------------------------------------------------------------------------- +namespace eval asn { + variable nonNumericChars {[^0-9 ]} +} +proc ::asn::asnNumericString {string} { + # the Numeric String type has tag 0x12 + variable nonNumericChars + if {[regexp $nonNumericChars $string]} { + return -code error "Illegal character in Numeric String." + } + + return [asnEncodeString 12 $string] +} +#---------------------------------------------------------------------- +# asnBMPString: Encode a Tcl string as Basic Multinligval (UCS2) string +#----------------------------------------------------------------------- +proc asn::asnBMPString {string} { + if {$::tcl_platform(byteOrder) eq "littleEndian"} { + set bytes "" + foreach {lo hi} [split [encoding convertto unicode $string] ""] { + append bytes $hi $lo + } + } else { + set bytes [encoding convertto unicode $string] + } + return [asnEncodeString 1e $bytes] +} +#--------------------------------------------------------------------------- +# asnUTF8String: encode tcl string as UTF8 String +#---------------------------------------------------------------------------- +proc asn::asnUTF8String {string} { + return [asnEncodeString 0c [encoding convertto utf-8 $string]] +} +#----------------------------------------------------------------------------- +# asnEncodeString : Encode an RestrictedCharacter String +#----------------------------------------------------------------------------- +proc ::asn::asnEncodeString {tag string} { + set len [string length $string] + return [binary format H2a*a$len $tag [asnLength $len] $string] +} + +#----------------------------------------------------------------------------- +# asnObjectIdentifier : Encode an Object Identifier value +#----------------------------------------------------------------------------- +proc ::asn::asnObjectIdentifier {oid} { + # the object identifier tag is 0x06 + + if {[llength $oid] < 2} { + return -code error "OID must have at least two subidentifiers." + } + + # basic check that it is valid + foreach identifier $oid { + if {$identifier < 0} { + return -code error \ + "Malformed OID. Identifiers must be positive Integers." + } + } + + if {[lindex $oid 0] > 2} { + return -code error "First subidentifier must be 0,1 or 2" + } + if {[lindex $oid 1] > 39} { + return -code error \ + "Second subidentifier must be between 0 and 39" + } + + # handle the special cases directly + switch [llength $oid] { + 2 { return [binary format H2H2c 06 01 \ + [expr {[lindex $oid 0]*40+[lindex $oid 1]}]] } + default { + # This can probably be written much shorter. + # Just a first try that works... + # + set octets [binary format c \ + [expr {[lindex $oid 0]*40+[lindex $oid 1]}]] + foreach identifier [lrange $oid 2 end] { + set d 128 + if {$identifier < 128} { + set subidentifier [list $identifier] + } else { + set subidentifier [list] + # find the largest divisor + + while {($identifier / $d) >= 128} { + set d [expr {$d * 128}] + } + # and construct the subidentifiers + set remainder $identifier + while {$d >= 128} { + set coefficient [expr {($remainder / $d) | 0x80}] + set remainder [expr {$remainder % $d}] + set d [expr {$d / 128}] + lappend subidentifier $coefficient + } + lappend subidentifier $remainder + } + append octets [binary format c* $subidentifier] + } + return [binary format H2a*a* 06 \ + [asnLength [string length $octets]] $octets] + } + } + +} + +#----------------------------------------------------------------------------- +# asnGetResponse : Read a ASN response from a channel. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetResponse {sock data_var} { + upvar $data_var data + + # We expect a sequence here (tag 0x30). The code below is an + # inlined replica of 'asnGetSequence', modified for reading from a + # channel instead of a string. + + set tag [read $sock 1] + + if {$tag == "\x30"} { + # The following code is a replica of 'asnGetLength', modified + # for reading the bytes from the channel instead of a string. + + set len1 [read $sock 1] + binary scan $len1 c num + set length [expr {($num + 0x100) % 0x100}] + + if {$length >= 0x080} { + # The byte the read is not the length, but a prefix, and + # the lower nibble tells us how many bytes follow. + + set len_length [expr {$length & 0x7f}] + + # BUG: We should not perform the value extraction for an + # BUG: improper length. It wastes cycles, and here it can + # BUG: cause us trouble, reading more data than there is + # BUG: on the channel. Depending on the channel + # BUG: configuration an attacker can induce us to block, + # BUG: causing a denial of service. + set lengthBytes [read $sock $len_length] + + switch $len_length { + 1 { + binary scan $lengthBytes c length + set length [expr {($length + 0x100) % 0x100}] + } + 2 { binary scan $lengthBytes S length } + 3 { binary scan \x00$lengthBytes I length } + 4 { binary scan $lengthBytes I length } + default { + return -code error \ + "length information too long ($len_length)" + } + } + } + + # Now that the length is known we get the remainder, + # i.e. payload, and construct proper in-memory BER encoded + # sequence. + + set rest [read $sock $length] + set data [binary format aa*a$length $tag [asnLength $length] $rest] + } else { + # Generate an error message if the data is not a sequence as + # we expected. + + set tag_hex "" + binary scan $tag H2 tag_hex + return -code error "unknown start tag [string length $tag] $tag_hex" + } +} + +#----------------------------------------------------------------------------- +# asnGetByte : Retrieve a single byte from the data (unsigned) +#----------------------------------------------------------------------------- + +proc ::asn::asnGetByte {data_var byte_var} { + upvar $data_var data $byte_var byte + + binary scan [string index $data 0] c byte + set byte [expr {($byte + 0x100) % 0x100}] + set data [string range $data 1 end] + + return +} + +#----------------------------------------------------------------------------- +# asnPeekByte : Retrieve a single byte from the data (unsigned) +# without removing it. +#----------------------------------------------------------------------------- + +proc ::asn::asnPeekByte {data_var byte_var} { + upvar $data_var data $byte_var byte + + binary scan [string index $data 0] c byte + set byte [expr {($byte + 0x100) % 0x100}] + + return +} + +#----------------------------------------------------------------------------- +# ansRetag: Remove an explicit tag with the real newTag +# +#----------------------------------------------------------------------------- +proc ::asn::asnRetag {data_var newTag} { + upvar 1 $data_var data + asnGetByte data tag + set data [binary format c $newTag]$data +} + +#----------------------------------------------------------------------------- +# asnGetBytes : Retrieve a block of 'length' bytes from the data. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetBytes {data_var length bytes_var} { + upvar $data_var data $bytes_var bytes + + incr length -1 + set bytes [string range $data 0 $length] + incr length + set data [string range $data $length end] + + return +} + + +#----------------------------------------------------------------------------- +# asnGetLength : Decode an ASN length value (See notes) +#----------------------------------------------------------------------------- + +proc ::asn::asnGetLength {data_var length_var} { + upvar $data_var data $length_var length + + asnGetByte data length + if {$length == 0x080} { + return -code error "Indefinite length BER encoding not yet supported" + } + if {$length > 0x080} { + # The retrieved byte is a prefix value, and the integer in the + # lower nibble tells us how many bytes were used to encode the + # length data following immediately after this prefix. + + set len_length [expr {$length & 0x7f}] + + if {[string length $data] < $len_length} { + return -code error \ + "length information invalid, not enough octets left" + } + + asnGetBytes data $len_length lengthBytes + + switch $len_length { + 1 { + # Efficiently coded data will not go through this + # path, as small length values can be coded directly, + # without a prefix. + + binary scan $lengthBytes c length + set length [expr {($length + 0x100) % 0x100}] + } + 2 { binary scan $lengthBytes S length + set length [expr {($length + 0x10000) % 0x10000}] + } + 3 { binary scan \x00$lengthBytes I length + set length [expr {($length + 0x1000000) % 0x1000000}] + } + 4 { binary scan $lengthBytes I length + set length [expr {(wide($length) + 0x100000000) % 0x100000000}] + } + default { + binary scan $lengthBytes H* hexstr + # skip leading zeros which are allowed by BER + set hexlen [string trimleft $hexstr 0] + # check if it fits into a 64-bit signed integer + if {[string length $hexlen] > 16} { + return -code error -errorcode {ARITH IOVERFLOW + {Length value too large for normal use, try asnGetBigLength}} \ + "Length value to large" + } elseif { [string length $hexlen] == 16 \ + && ([string index $hexlen 0] & 0x8)} { + # check most significant bit, if set we need bignum + return -code error -errorcode {ARITH IOVERFLOW + {Length value too large for normal use, try asnGetBigLength}} \ + "Length value to large" + } else { + scan $hexstr "%lx" length + } + } + } + } + return +} + + +#----------------------------------------------------------------------------- +# asnGetBigLength : Retrieve a length that can not be represented in 63-bit +#----------------------------------------------------------------------------- + +proc ::asn::asnGetBigLength {data_var biglength_var} { + + # Does any real world code really need this? + # If we encounter this, we are doomed to fail anyway, + # (there would be an Exabyte inside the data_var, ) + # + # So i implement it just for completness. + # + package require math::bignum + + upvar $data_var data $length_var length + + asnGetByte data length + if {$length == 0x080} { + return -code error "Indefinite length BER encoding not yet supported" + } + if {$length > 0x080} { + # The retrieved byte is a prefix value, and the integer in the + # lower nibble tells us how many bytes were used to encode the + # length data following immediately after this prefix. + + set len_length [expr {$length & 0x7f}] + + if {[string length $data] < $len_length} { + return -code error \ + "length information invalid, not enough octets left" + } + + asnGetBytes data $len_length lengthBytes + binary scan $lengthBytes H* hexlen + set length [math::bignum::fromstr $hexlen 16] + } + return +} + +#----------------------------------------------------------------------------- +# asnGetInteger : Retrieve integer. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetInteger {data_var int_var} { + # Tag is 0x02. + + upvar $data_var data $int_var int + + asnGetByte data tag + + if {$tag != 0x02} { + return -code error \ + [format "Expected Integer (0x02), but got %02x" $tag] + } + + asnGetLength data len + asnGetBytes data $len integerBytes + + set int ? + + switch $len { + 1 { binary scan $integerBytes c int } + 2 { binary scan $integerBytes S int } + 3 { + # check for negative int and pad + scan [string index $integerBytes 0] %c byte + if {$byte & 128} { + binary scan \xff$integerBytes I int + } else { + binary scan \x00$integerBytes I int + } + } + 4 { binary scan $integerBytes I int } + 5 - + 6 - + 7 - + 8 { + # check for negative int and pad + scan [string index $integerBytes 0] %c byte + if {$byte & 128} { + set pad [string repeat \xff [expr {8-$len}]] + } else { + set pad [string repeat \x00 [expr {8-$len}]] + } + binary scan $pad$integerBytes W int + } + default { + # Too long, or prefix coding was used. + return -code error "length information too long" + } + } + return +} + +#----------------------------------------------------------------------------- +# asnGetBigInteger : Retrieve a big integer. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetBigInteger {data_var bignum_var} { + # require math::bignum only if it is used + package require math::bignum + + # Tag is 0x02. We expect that the length of the integer is coded with + # maximal efficiency, i.e. without a prefix 0x81 prefix. If a prefix + # is used this decoder will fail. + + upvar $data_var data $bignum_var bignum + + asnGetByte data tag + + if {$tag != 0x02} { + return -code error \ + [format "Expected Integer (0x02), but got %02x" $tag] + } + + asnGetLength data len + asnGetBytes data $len integerBytes + + binary scan $integerBytes H* hex + set bignum [math::bignum::fromstr $hex 16] + set bits [math::bignum::bits $bignum] + set exp [math::bignum::pow \ + [math::bignum::fromstr 2] \ + [math::bignum::fromstr $bits]] + set big [math::bignum::sub $bignum $exp] + set bignum $big + + return +} + + + +#----------------------------------------------------------------------------- +# asnGetEnumeration : Retrieve an enumeration id +#----------------------------------------------------------------------------- + +proc ::asn::asnGetEnumeration {data_var enum_var} { + # This is like 'asnGetInteger', except for a different tag. + + upvar $data_var data $enum_var enum + + asnGetByte data tag + + if {$tag != 0x0a} { + return -code error \ + [format "Expected Enumeration (0x0a), but got %02x" $tag] + } + + asnGetLength data len + asnGetBytes data $len integerBytes + set enum ? + + switch $len { + 1 { binary scan $integerBytes c enum } + 2 { binary scan $integerBytes S enum } + 3 { binary scan \x00$integerBytes I enum } + 4 { binary scan $integerBytes I enum } + default { + return -code error "length information too long" + } + } + return +} + +#----------------------------------------------------------------------------- +# asnGetOctetString : Retrieve arbitrary string. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetOctetString {data_var string_var} { + # Here we need the full decoder for length data. + + upvar $data_var data $string_var string + + asnGetByte data tag + if {$tag != 0x04} { + return -code error \ + [format "Expected Octet String (0x04), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length temp + set string $temp + return +} + +#----------------------------------------------------------------------------- +# asnGetSequence : Retrieve Sequence data for further decoding. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetSequence {data_var sequence_var} { + # Here we need the full decoder for length data. + + upvar $data_var data $sequence_var sequence + + asnGetByte data tag + if {$tag != 0x030} { + return -code error \ + [format "Expected Sequence (0x30), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length temp + set sequence $temp + return +} + +#----------------------------------------------------------------------------- +# asnGetSet : Retrieve Set data for further decoding. +#----------------------------------------------------------------------------- + +proc ::asn::asnGetSet {data_var set_var} { + # Here we need the full decoder for length data. + + upvar $data_var data $set_var set + + asnGetByte data tag + if {$tag != 0x031} { + return -code error \ + [format "Expected Set (0x31), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length temp + set set $temp + return +} + +#----------------------------------------------------------------------------- +# asnGetApplication +#----------------------------------------------------------------------------- + +proc ::asn::asnGetApplication {data_var appNumber_var {content_var {}} {constructed_var {}}} { + upvar $data_var data $appNumber_var appNumber + + asnGetByte data tag + asnGetLength data length + + if {($tag & 0xC0) != 0x040} { + return -code error \ + [format "Expected Application (0x60 or 0x40), but got %02x" $tag] + } + set appNumber [expr {$tag & 0x1F}] + if {[string length $constructed_var]} { + upvar 1 $constructed_var constructed + set constructed [expr {$tag & 0x20}] + } + if {[string length $content_var]} { + upvar 1 $content_var content + asnGetBytes data $length content + } + return +} + +#----------------------------------------------------------------------------- +# asnGetBoolean: decode a boolean value +#----------------------------------------------------------------------------- + +proc asn::asnGetBoolean {data_var bool_var} { + upvar $data_var data $bool_var bool + + asnGetByte data tag + if {$tag != 0x01} { + return -code error \ + [format "Expected Boolean (0x01), but got %02x" $tag] + } + + asnGetLength data length + asnGetByte data byte + set bool [expr {$byte == 0 ? 0 : 1}] + return +} + +#----------------------------------------------------------------------------- +# asnGetUTCTime: Extract an UTC Time string from the data. Returns a string +# representing an UTC Time. +# +#----------------------------------------------------------------------------- + +proc asn::asnGetUTCTime {data_var utc_var} { + upvar $data_var data $utc_var utc + + asnGetByte data tag + if {$tag != 0x17} { + return -code error \ + [format "Expected UTCTime (0x17), but got %02x" $tag] + } + + asnGetLength data length + asnGetBytes data $length bytes + + # this should be ascii, make it explicit + set bytes [encoding convertfrom ascii $bytes] + binary scan $bytes a* utc + + return +} + + +#----------------------------------------------------------------------------- +# asnGetBitString: Extract a Bit String value (a string of 0/1s) from the +# ASN.1 data. +# +#----------------------------------------------------------------------------- + +proc asn::asnGetBitString {data_var bitstring_var} { + upvar $data_var data $bitstring_var bitstring + + asnGetByte data tag + if {$tag != 0x03} { + return -code error \ + [format "Expected Bit String (0x03), but got %02x" $tag] + } + + asnGetLength data length + # get the number of padding bits used at the end + asnGetByte data padding + incr length -1 + asnGetBytes data $length bytes + binary scan $bytes B* bits + + # cut off the padding bits + set bits [string range $bits 0 end-$padding] + set bitstring $bits +} + +#----------------------------------------------------------------------------- +# asnGetObjectIdentifier: Decode an ASN.1 Object Identifier (OID) into +# a Tcl list of integers. +#----------------------------------------------------------------------------- + +proc asn::asnGetObjectIdentifier {data_var oid_var} { + upvar $data_var data $oid_var oid + + asnGetByte data tag + if {$tag != 0x06} { + return -code error \ + [format "Expected Object Identifier (0x06), but got %02x" $tag] + } + asnGetLength data length + + # the first byte encodes the OID parts in position 0 and 1 + asnGetByte data val + set oid [expr {$val / 40}] + lappend oid [expr {$val % 40}] + incr length -1 + + # the next bytes encode the remaining parts of the OID + set bytes [list] + set incomplete 0 + while {$length} { + asnGetByte data octet + incr length -1 + if {$octet < 128} { + set oidval $octet + set mult 128 + foreach byte $bytes { + if {$byte != {}} { + incr oidval [expr {$mult*$byte}] + set mult [expr {$mult*128}] + } + } + lappend oid $oidval + set bytes [list] + set incomplete 0 + } else { + set byte [expr {$octet-128}] + set bytes [concat [list $byte] $bytes] + set incomplete 1 + } + } + if {$incomplete} { + return -code error "OID Data is incomplete, not enough octets." + } + return +} + +#----------------------------------------------------------------------------- +# asnGetContext: Decode an explicit context tag +# +#----------------------------------------------------------------------------- + +proc ::asn::asnGetContext {data_var contextNumber_var {content_var {}} {constructed_var {}}} { + upvar 1 $data_var data $contextNumber_var contextNumber + + asnGetByte data tag + asnGetLength data length + + if {($tag & 0xC0) != 0x080} { + return -code error \ + [format "Expected Context (0xa0 or 0x80), but got %02x" $tag] + } + set contextNumber [expr {$tag & 0x1F}] + if {[string length $constructed_var]} { + upvar 1 $constructed_var constructed + set constructed [expr {$tag & 0x20}] + } + if {[string length $content_var]} { + upvar 1 $content_var content + asnGetBytes data $length content + } + return +} + + +#----------------------------------------------------------------------------- +# asnGetNumericString: Decode a Numeric String from the data +#----------------------------------------------------------------------------- + +proc ::asn::asnGetNumericString {data_var print_var} { + upvar 1 $data_var data $print_var print + + asnGetByte data tag + if {$tag != 0x12} { + return -code error \ + [format "Expected Numeric String (0x12), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + set print [encoding convertfrom ascii $string] + return +} + +#----------------------------------------------------------------------------- +# asnGetPrintableString: Decode a Printable String from the data +#----------------------------------------------------------------------------- + +proc ::asn::asnGetPrintableString {data_var print_var} { + upvar $data_var data $print_var print + + asnGetByte data tag + if {$tag != 0x13} { + return -code error \ + [format "Expected Printable String (0x13), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + set print [encoding convertfrom ascii $string] + return +} + +#----------------------------------------------------------------------------- +# asnGetIA5String: Decode a IA5(ASCII) String from the data +#----------------------------------------------------------------------------- + +proc ::asn::asnGetIA5String {data_var print_var} { + upvar $data_var data $print_var print + + asnGetByte data tag + if {$tag != 0x16} { + return -code error \ + [format "Expected IA5 String (0x16), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + set print [encoding convertfrom ascii $string] + return +} +#------------------------------------------------------------------------ +# asnGetBMPString: Decode Basic Multiningval (UCS2 string) from data +#------------------------------------------------------------------------ +proc asn::asnGetBMPString {data_var print_var} { + upvar $data_var data $print_var print + asnGetByte data tag + if {$tag != 0x1e} { + return -code error \ + [format "Expected BMP String (0x1e), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + if {$::tcl_platform(byteOrder) eq "littleEndian"} { + set str2 "" + foreach {hi lo} [split $string ""] { + append str2 $lo $hi + } + } else { + set str2 $string + } + set print [encoding convertfrom unicode $str2] + return +} +#------------------------------------------------------------------------ +# asnGetUTF8String: Decode UTF8 string from data +#------------------------------------------------------------------------ +proc asn::asnGetUTF8String {data_var print_var} { + upvar $data_var data $print_var print + asnGetByte data tag + if {$tag != 0x0c} { + return -code error \ + [format "Expected UTF8 String (0x0c), but got %02x" $tag] + } + asnGetLength data length + asnGetBytes data $length string + #there should be some error checking to see if input is + #properly-formatted utf8 + set print [encoding convertfrom utf-8 $string] + + return +} +#----------------------------------------------------------------------------- +# asnGetNull: decode a NULL value +#----------------------------------------------------------------------------- + +proc ::asn::asnGetNull {data_var} { + upvar $data_var data + + asnGetByte data tag + if {$tag != 0x05} { + return -code error \ + [format "Expected NULL (0x05), but got %02x" $tag] + } + + asnGetLength data length + asnGetBytes data $length bytes + + # we do not check the null data, all bytes must be 0x00 + + return +} + +#---------------------------------------------------------------------------- +# MultiType string routines +#---------------------------------------------------------------------------- + +namespace eval asn { + variable stringTypes + array set stringTypes { + 12 NumericString + 13 PrintableString + 16 IA5String + 1e BMPString + 0c UTF8String + 14 T61String + 15 VideotexString + 1a VisibleString + 1b GeneralString + 1c UniversalString + } + variable defaultStringType UTF8 +} +#--------------------------------------------------------------------------- +# asnGetString - get readable string automatically detecting its type +#--------------------------------------------------------------------------- +proc ::asn::asnGetString {data_var print_var {type_var {}}} { + variable stringTypes + upvar $data_var data $print_var print + asnPeekByte data tag + set tag [format %02x $tag] + if {![info exists stringTypes($tag)]} { + return -code error "Expected one of string types, but got $tag" + } + asnGet$stringTypes($tag) data print + if {[string length $type_var]} { + upvar $type_var type + set type $stringTypes($tag) + } +} +#--------------------------------------------------------------------- +# defaultStringType - set or query default type for unrestricted strings +#--------------------------------------------------------------------- +proc ::asn::defaultStringType {{type {}}} { + variable defaultStringType + if {![string length $type]} { + return $defaultStringType + } + if {$type ne "BMP" && $type ne "UTF8"} { + return -code error "Invalid default string type. Should be one of BMP, UTF8" + } + set defaultStringType $type + return +} + +#--------------------------------------------------------------------------- +# asnString - encode readable string into most restricted type possible +#--------------------------------------------------------------------------- + +proc ::asn::asnString {string} { + variable nonPrintableChars + variable nonNumericChars + if {[string length $string]!=[string bytelength $string]} { + # There are non-ascii character + variable defaultStringType + return [asn${defaultStringType}String $string] + } elseif {![regexp $nonNumericChars $string]} { + return [asnNumericString $string] + } elseif {![regexp $nonPrintableChars $string]} { + return [asnPrintableString $string] + } else { + return [asnIA5String $string] + } +} + +#----------------------------------------------------------------------------- +package provide asn 0.7.1 + diff --git a/tcl_tests/base64.tcl b/tcl_tests/base64.tcl new file mode 100644 index 0000000..21f1d43 --- /dev/null +++ b/tcl_tests/base64.tcl @@ -0,0 +1,326 @@ +# base64.tcl -- +# +# Encode/Decode base64 for a string +# Stephen Uhler / Brent Welch (c) 1997 Sun Microsystems +# The decoder was done for exmh by Chris Garrigues +# +# Copyright (c) 1998-2000 by Ajuba Solutions. +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# RCS: @(#) $Id: base64.tcl,v 1.1 2012-04-04 10:50:38 igus Exp $ + +# Version 1.0 implemented Base64_Encode, Base64_Decode +# Version 2.0 uses the base64 namespace +# Version 2.1 fixes various decode bugs and adds options to encode +# Version 2.2 is much faster, Tcl8.0 compatible +# Version 2.2.1 bugfixes +# Version 2.2.2 bugfixes +# Version 2.3 bugfixes and extended to support Trf + +# @mdgen EXCLUDE: base64c.tcl + +package require Tcl 8.2 +namespace eval ::base64 { + namespace export encode decode +} + +if {![catch {package require Trf 2.0}]} { + # Trf is available, so implement the functionality provided here + # in terms of calls to Trf for speed. + + # ::base64::encode -- + # + # Base64 encode a given string. + # + # Arguments: + # args ?-maxlen maxlen? ?-wrapchar wrapchar? string + # + # If maxlen is 0, the output is not wrapped. + # + # Results: + # A Base64 encoded version of $string, wrapped at $maxlen characters + # by $wrapchar. + + proc ::base64::encode {args} { + # Set the default wrapchar and maximum line length to match the output + # of GNU uuencode 4.2. Various RFCs allow for different wrapping + # characters and wraplengths, so these may be overridden by command line + # options. + set wrapchar "\n" + set maxlen 60 + + if { [llength $args] == 0 } { + error "wrong # args: should be \"[lindex [info level 0] 0]\ + ?-maxlen maxlen? ?-wrapchar wrapchar? string\"" + } + + set optionStrings [list "-maxlen" "-wrapchar"] + for {set i 0} {$i < [llength $args] - 1} {incr i} { + set arg [lindex $args $i] + set index [lsearch -glob $optionStrings "${arg}*"] + if { $index == -1 } { + error "unknown option \"$arg\": must be -maxlen or -wrapchar" + } + incr i + if { $i >= [llength $args] - 1 } { + error "value for \"$arg\" missing" + } + set val [lindex $args $i] + + # The name of the variable to assign the value to is extracted + # from the list of known options, all of which have an + # associated variable of the same name as the option without + # a leading "-". The [string range] command is used to strip + # of the leading "-" from the name of the option. + # + # FRINK: nocheck + set [string range [lindex $optionStrings $index] 1 end] $val + } + + # [string is] requires Tcl8.2; this works with 8.0 too + if {[catch {expr {$maxlen % 2}}]} { + error "expected integer but got \"$maxlen\"" + } + + set string [lindex $args end] + set result [::base64 -mode encode -- $string] + set result [string map [list \n ""] $result] + + if {$maxlen > 0} { + set res "" + set edge [expr {$maxlen - 1}] + while {[string length $result] > $maxlen} { + append res [string range $result 0 $edge]$wrapchar + set result [string range $result $maxlen end] + } + if {[string length $result] > 0} { + append res $result + } + set result $res + } + + return $result + } + + # ::base64::decode -- + # + # Base64 decode a given string. + # + # Arguments: + # string The string to decode. Characters not in the base64 + # alphabet are ignored (e.g., newlines) + # + # Results: + # The decoded value. + + proc ::base64::decode {string} { + regsub -all {\s} $string {} string + ::base64 -mode decode -- $string + } + +} else { + # Without Trf use a pure tcl implementation + + namespace eval base64 { + variable base64 {} + variable base64_en {} + + # We create the auxiliary array base64_tmp, it will be unset later. + + set i 0 + foreach char {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ + a b c d e f g h i j k l m n o p q r s t u v w x y z \ + 0 1 2 3 4 5 6 7 8 9 + /} { + set base64_tmp($char) $i + lappend base64_en $char + incr i + } + + # + # Create base64 as list: to code for instance C<->3, specify + # that [lindex $base64 67] be 3 (C is 67 in ascii); non-coded + # ascii chars get a {}. we later use the fact that lindex on a + # non-existing index returns {}, and that [expr {} < 0] is true + # + + # the last ascii char is 'z' + scan z %c len + for {set i 0} {$i <= $len} {incr i} { + set char [format %c $i] + set val {} + if {[info exists base64_tmp($char)]} { + set val $base64_tmp($char) + } else { + set val {} + } + lappend base64 $val + } + + # code the character "=" as -1; used to signal end of message + scan = %c i + set base64 [lreplace $base64 $i $i -1] + + # remove unneeded variables + unset base64_tmp i char len val + + namespace export encode decode + } + + # ::base64::encode -- + # + # Base64 encode a given string. + # + # Arguments: + # args ?-maxlen maxlen? ?-wrapchar wrapchar? string + # + # If maxlen is 0, the output is not wrapped. + # + # Results: + # A Base64 encoded version of $string, wrapped at $maxlen characters + # by $wrapchar. + + proc ::base64::encode {args} { + set base64_en $::base64::base64_en + + # Set the default wrapchar and maximum line length to match the output + # of GNU uuencode 4.2. Various RFCs allow for different wrapping + # characters and wraplengths, so these may be overridden by command line + # options. + set wrapchar "\n" + set maxlen 60 + + if { [llength $args] == 0 } { + error "wrong # args: should be \"[lindex [info level 0] 0]\ + ?-maxlen maxlen? ?-wrapchar wrapchar? string\"" + } + + set optionStrings [list "-maxlen" "-wrapchar"] + for {set i 0} {$i < [llength $args] - 1} {incr i} { + set arg [lindex $args $i] + set index [lsearch -glob $optionStrings "${arg}*"] + if { $index == -1 } { + error "unknown option \"$arg\": must be -maxlen or -wrapchar" + } + incr i + if { $i >= [llength $args] - 1 } { + error "value for \"$arg\" missing" + } + set val [lindex $args $i] + + # The name of the variable to assign the value to is extracted + # from the list of known options, all of which have an + # associated variable of the same name as the option without + # a leading "-". The [string range] command is used to strip + # of the leading "-" from the name of the option. + # + # FRINK: nocheck + set [string range [lindex $optionStrings $index] 1 end] $val + } + + # [string is] requires Tcl8.2; this works with 8.0 too + if {[catch {expr {$maxlen % 2}}]} { + error "expected integer but got \"$maxlen\"" + } + + set string [lindex $args end] + + set result {} + set state 0 + set length 0 + + + # Process the input bytes 3-by-3 + + binary scan $string c* X + foreach {x y z} $X { + # Do the line length check before appending so that we don't get an + # extra newline if the output is a multiple of $maxlen chars long. + if {$maxlen && $length >= $maxlen} { + append result $wrapchar + set length 0 + } + + append result [lindex $base64_en [expr {($x >>2) & 0x3F}]] + if {$y != {}} { + append result [lindex $base64_en [expr {(($x << 4) & 0x30) | (($y >> 4) & 0xF)}]] + if {$z != {}} { + append result \ + [lindex $base64_en [expr {(($y << 2) & 0x3C) | (($z >> 6) & 0x3)}]] + append result [lindex $base64_en [expr {($z & 0x3F)}]] + } else { + set state 2 + break + } + } else { + set state 1 + break + } + incr length 4 + } + if {$state == 1} { + append result [lindex $base64_en [expr {(($x << 4) & 0x30)}]]== + } elseif {$state == 2} { + append result [lindex $base64_en [expr {(($y << 2) & 0x3C)}]]= + } + return $result + } + + # ::base64::decode -- + # + # Base64 decode a given string. + # + # Arguments: + # string The string to decode. Characters not in the base64 + # alphabet are ignored (e.g., newlines) + # + # Results: + # The decoded value. + + proc ::base64::decode {string} { + if {[string length $string] == 0} {return ""} + + set base64 $::base64::base64 + set output "" ; # Fix for [Bug 821126] + + binary scan $string c* X + foreach x $X { + set bits [lindex $base64 $x] + if {$bits >= 0} { + if {[llength [lappend nums $bits]] == 4} { + foreach {v w z y} $nums break + set a [expr {($v << 2) | ($w >> 4)}] + set b [expr {(($w & 0xF) << 4) | ($z >> 2)}] + set c [expr {(($z & 0x3) << 6) | $y}] + append output [binary format ccc $a $b $c] + set nums {} + } + } elseif {$bits == -1} { + # = indicates end of data. Output whatever chars are left. + # The encoding algorithm dictates that we can only have 1 or 2 + # padding characters. If x=={}, we have 12 bits of input + # (enough for 1 8-bit output). If x!={}, we have 18 bits of + # input (enough for 2 8-bit outputs). + + foreach {v w z} $nums break + set a [expr {($v << 2) | (($w & 0x30) >> 4)}] + if {$z == {}} { + append output [binary format c $a ] + } else { + set b [expr {(($w & 0xF) << 4) | (($z & 0x3C) >> 2)}] + append output [binary format cc $a $b] + } + break + } else { + # RFC 2045 says that line breaks and other characters not part + # of the Base64 alphabet must be ignored, and that the decoder + # can optionally emit a warning or reject the message. We opt + # not to do so, but to just ignore the character. + continue + } + } + return $output + } +} + +package provide base64 2.3.2 diff --git a/tcl_tests/ca.try b/tcl_tests/ca.try new file mode 100644 index 0000000..c84088c --- /dev/null +++ b/tcl_tests/ca.try @@ -0,0 +1,125 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на команду ca" + +if {[info exists env(ALG_LIST)]} { + set alg_pair_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_pair_list {gost2001:A {gost2001:B} gost2012_256:A {gost2012_256:C} gost2012_512:B {gost2012_256:B gost2012_512:A}}} + "open" {set alg_pair_list {gost2001:A {gost2001:B} gost2012_256:A {gost2012_256:C} gost2012_512:B {gost2012_256:B gost2012_512:A}}} + } +} + +foreach {ca_alg alg_list} $alg_pair_list { + set ca_alg_fn [string map {":" "_"} $ca_alg] + +test "Creating CA" { + makeCA ${testname}CA-$ca_alg_fn $ca_alg +} 0 1 + +set serial_list "" + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set userdir U_ca_${alg_fn}_for_${ca_alg_fn} + +#Depends on previous +test "Creating user request" { + makeUser $userdir $alg +} 0 1 + +test -skip {![file exists $userdir/req.pem]} "Registering cert at CA" { + grep Sign [openssl "ca -config $::test::ca/ca.conf -in $userdir/req.pem -out $userdir/cert.pem -batch -notext"] +} 0 "Signature ok +" +if [file exists $userdir/req.pem] { +set cert [getFile $userdir/req.pem] +makeFile $userdir/broken.pem [hackPem "Cryptocom" $cert "Kriptokom"] +} +test -skip {![file exists $userdir/broken.pem]} "Registering broken request at ca" { + grep Sign [openssl "ca -config $::test::ca/ca.conf -in $userdir/broken.pem -out $userdir/badcert.pem -batch"] +} 0 "Signature did not match the certificate request +" + +#test "Generating self-signed CA certificate" { +# +#} 0 "not written" + +test "Revoking certificate" { + set revoking_cert $::test::ca/newcerts/[string trim [getFile $::test::ca/serial.old]].pem + append serial_list [regsub "serial=" [openssl "x509 -in $revoking_cert -noout -serial"] " Serial Number: "] + grep "Data Base" [openssl "ca -crl_reason keyCompromize -crl_compromise [clock\ + format [clock seconds] -format %Y%m%d%H%M%SZ] -revoke $revoking_cert -config $::test::ca/ca.conf"] +} 0 "Data Base Updated +" +test -createsfiles test.crl "Generating CRL" { + openssl "ca -gencrl -config $::test::ca/ca.conf -out test.crl" + file copy -force $::test::ca/cacert.pem test_crl_cacert.pem + file exist test.crl +} 0 1 + + +test -skip {![file exists test.crl]} "Displaying CRL" { + grep "(Serial|Version|Signature Alg|Issuer)" [openssl "crl -text -noout -in test.crl"] +} 0 " Version 2 (0x1) + Signature Algorithm: [hash_with_sign_long_name $ca_alg] + Issuer: C = RU, L = Moscow, CN = Test CA $ca_alg, O = Cryptocom, OU = OpenSSL CA, emailAddress = openssl@cryptocom.ru +$serial_list Signature Algorithm: [hash_with_sign_long_name $ca_alg] +" + +test -skip {![file exists test.crl]} "Verifying CRL OK" { + grep verify [openssl "crl -in test.crl -noout -CAfile $::test::ca/cacert.pem"] +} 0 "verify OK +" + +test -skip {![file exists test.crl]} "Verifying corrupted CRL" { + makeFile "badcrl.pem" [hackPem "\01\x1E" [getFile test.crl] "\01\0"] + grep verify [openssl "crl -in badcrl.pem -noout -CAfile $::test::ca/cacert.pem"] +} 0 "verify failure +" + +test "Verifying CA certificate" { + grep "(cacert.pem|error|OK)" [openssl "verify $::test::ca/cacert.pem"] +} 1 "error $::test::ca/cacert.pem: verification failed +STDERR CONTENTS: +C = RU, L = Moscow, CN = Test CA $ca_alg, O = Cryptocom, OU = OpenSSL CA, emailAddress = openssl@cryptocom.ru +error 18 at 0 depth lookup: self signed certificate" + + + +test "Verifying certificate" { + grep "cert.pem" [openssl "verify -CAfile $::test::ca/cacert.pem $userdir/cert.pem"] +} 0 "$userdir/cert.pem: OK +" + +test "Verifying bad certificate" { + makeFile "badcert.pem" [hackPem "Team" [getFile $userdir/cert.pem] "meat"] + openssl "verify -CAfile $::test::ca/cacert.pem badcert.pem" +} 1 "certificate signature failure" + +test "Verifying revoked certificate" { + makeFile ca_crl.pem "[getFile $::test::ca/cacert.pem]\n[getFile test.crl]" + openssl "verify -crl_check -CAfile ca_crl.pem $userdir/cert.pem" +} 1 "certificate revoked" + +test "Create a PKCS#7 structure from a certificate and CRL" { + openssl "crl2pkcs7 -in test.crl -certfile $userdir/cert.pem -out $userdir/p7.pem" + extract_oids $userdir/p7.pem PEM +} 0 [mkObjList [hash_with_sign_long_name $ca_alg] [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [hash_with_sign_long_name $ca_alg] [hash_with_sign_long_name $ca_alg] [hash_with_sign_long_name $ca_alg]] + +test "Creates a PKCS#7 structure without CRL" { + openssl "crl2pkcs7 -nocrl -certfile $userdir/cert.pem -certfile $::test::ca/cacert.pem -out $userdir/p7_nocrl.pem" + extract_oids $userdir/p7_nocrl.pem PEM +} 0 [mkObjList [hash_with_sign_long_name $ca_alg] [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [hash_with_sign_long_name $ca_alg] [hash_with_sign_long_name $ca_alg] [alg_long_name $ca_alg] [pubkey_long_name $ca_alg] [param_hash_long_name [param_hash $ca_alg]] [hash_with_sign_long_name $ca_alg]] + +} + +} + +end_tests diff --git a/tcl_tests/calchash.tcl b/tcl_tests/calchash.tcl new file mode 100755 index 0000000..ec68961 --- /dev/null +++ b/tcl_tests/calchash.tcl @@ -0,0 +1,48 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require test + +if {$::tcl_platform(platform) eq "windows"} { + set prefix {//laputa/dist/magpro/FSB_CryptoPack_21.1/binaries} +} else { + set prefix {/net/laputa/pub/magpro/FSB_CryptoPack_21.1/binaries} +} +set PREFIX_ENV_NAME CALCHASH_PREFIX +if {$argc != 1} { + puts stderr "Usage $argv0 path" + puts stderr "This script tests programms prefix/path/calchach and prefix/path/gostsum." + puts stderr "Defauld prefix is $prefix" + puts stderr "Prefix can be changes by envirament veriable $PREFIX_ENV_NAME" + exit 1 +} + +if {[info exist env($PREFIX_ENV_NAME)]} { + set prefix $env($PREFIX_ENV_NAME) +} +set path [lindex $argv 0] + +set testdir [exec hostname]-hashes +puts $testdir +catch {file delete -force $testdir} +file mkdir $testdir +cd $testdir + +start_tests "Тесты для программ calchash и gostsum" + +test -createsfiles dgst.dat "calchash" { + makeFile dgst.dat [string repeat "Test data to digest.\n" 100] binary + string match *DB9232D96CAE7AABA817350EF6CF4C25604D8FD36965F78CEB3CE59FD31CCB2A [exec $prefix/$path/calchash dgst.dat] +} 0 1 + +test -platform unix "gostsum (paramset cryptopro-A)" { + exec $prefix/$path/gostsum dgst.dat +} 0 "5c8621c036f8636fa3ea711a78e5051f607c87b4b715482af74b2b1cce62e442 dgst.dat" + + +test -platform unix "gostsum -t (paramset test)" { + exec $prefix/$path/gostsum -t dgst.dat +} 0 "db9232d96cae7aaba817350ef6cf4c25604d8fd36965f78ceb3ce59fd31ccb2a dgst.dat" + + +end_tests diff --git a/tcl_tests/calcstat b/tcl_tests/calcstat new file mode 100644 index 0000000..3e59eb6 --- /dev/null +++ b/tcl_tests/calcstat @@ -0,0 +1,60 @@ +#!/usr/bin/tclsh +if {$tcl_platform(platform) == "unix"} { + fconfigure stdout -translation lf +} +lappend auto_path [file dirname [info script]] +proc rus {string} { + return [encoding convertfrom cp1251 [encoding convertto $string]] +} +proc compare_order {el1 el2} { + global order + return [expr {$order($el1)-$order($el2)}] +} +set statsfile stats +if {$argc} {set statsfile [lindex $argv 0]} +set f [open $statsfile] +fconfigure $f -encoding cp1251 +set ordno 0 +while {[gets $f line] >=0} { + set script [lindex $line 0] + set a($script) [lrange $line 1 end] + if {![info exists order($script)]} { + set order($script) [incr ordno] + } +} +close $f + +proc output {line} { + global out + puts $line + if {[info exists out]} { + puts $out $line + } +} + +if {$argc > 1} { + set out [open [lindex $argv 1] w] + fconfigure $out -encoding cp1251 +} + +output [format "%-12s %-41s%5s %4s %4s %4s %4s" File "Test name" Total ok fail skip ign] +output [string repeat "-" 79] +array set gross {total 0 ok 0 fail 0 p_skip 0 c_skip 0} + + +foreach script [lsort -command compare_order [array names a] ] { + foreach {name total ok fail p_skip c_skip} $a($script) break + output [format "%-12s %-41s%5d %4d %4d %4d %4d" [string range [file tail [file rootname $script]] 0 11] [string range $name 0 40] $total $ok $fail $p_skip $c_skip] + incr gross(total) $total + incr gross(ok) $ok + incr gross(fail) $fail + incr gross(p_skip) $p_skip + incr gross(c_skip) $c_skip +} + +output [string repeat "-" 79] +output [format "%-54s%5d %4d %4d %4d %4d" Total $gross(total) $gross(ok) $gross(fail) $gross(p_skip) $gross(c_skip)] + +if {$gross(fail)} { + exit 1 +} diff --git a/tcl_tests/cbc0.enc b/tcl_tests/cbc0.enc new file mode 100644 index 0000000..8ef7d15 --- /dev/null +++ b/tcl_tests/cbc0.enc @@ -0,0 +1,2 @@ +Salted__»d°å„½1Ž‚ÐÞo‡tV‚ºÙ‹ +"h;ó›hú‡ \ No newline at end of file diff --git a/tcl_tests/cbc1.enc b/tcl_tests/cbc1.enc new file mode 100644 index 0000000..41872cd --- /dev/null +++ b/tcl_tests/cbc1.enc @@ -0,0 +1,2 @@ +‚ÐÞo‡tV‚ºÙ‹ +"h;ó›hú‡ \ No newline at end of file diff --git a/tcl_tests/cfb0.enc b/tcl_tests/cfb0.enc new file mode 100644 index 0000000..b254385 --- /dev/null +++ b/tcl_tests/cfb0.enc @@ -0,0 +1,2 @@ +Salted__©„ˆ¦8>õ +i¹«ÊS#Õ@ùÃðgu“ï$ \ No newline at end of file diff --git a/tcl_tests/cfb1.enc b/tcl_tests/cfb1.enc new file mode 100644 index 0000000..e103ace --- /dev/null +++ b/tcl_tests/cfb1.enc @@ -0,0 +1,2 @@ +õ +i¹«ÊS#Õ@ùÃðgu“ï$ \ No newline at end of file diff --git a/tcl_tests/ciphers.try b/tcl_tests/ciphers.try new file mode 100644 index 0000000..ae75759 --- /dev/null +++ b/tcl_tests/ciphers.try @@ -0,0 +1,38 @@ +#!/usr/bin/tclsh +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Тесты на команду ciphers" + +proc find_ciphers {openssl_args globpattern} { + set found [lsort [lsearch -all -inline [split [string trim [grep $globpattern [openssl $openssl_args] ] "\n" ] : ] "$globpattern*"] ] + return $found +} + +test "Проверяем поддержку российских алгоритмов в tls" { + find_ciphers "ciphers" "GOST" +} 0 {GOST2001-GOST89-GOST89 GOST2012-GOST8912-GOST8912} + +test "Проверяем поддержку российских алгоритмов без шифрования в tls" { + find_ciphers "ciphers NULL" "GOST" +} 0 {GOST2001-NULL-GOST94 GOST2012-NULL-GOST12} + +#test "Проверяем отсутствие российских алгоритмов в ssl2" { +# find_ciphers "ciphers -ssl2" "GOST" +#} 0 "" + +#test "Проверяем работоспособность команды ciphers" { +# find_ciphers "ciphers AES" "AES" +#} 0 {"ADH-AES256-SHA" +#"DHE-RSA-AES256-SHA" +#"DHE-DSS-AES256-SHA" +#"AES256-SHA" +#"ADH-AES128-SHA" +#"DHE-RSA-AES128-SHA" +#"DHE-DSS-AES128-SHA" +#"AES128-SHA" +#} + + +end_tests + diff --git a/tcl_tests/client.try b/tcl_tests/client.try new file mode 100644 index 0000000..d6abcb3 --- /dev/null +++ b/tcl_tests/client.try @@ -0,0 +1,143 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] + +package require ossltest + +if {$argc != 1} { + puts stderr "Usage $argv0 cipher-list-file" + exit 1 +} + +array set protos { + SSLv2 -ssl2 + SSLv3 -ssl3 + TLSv1 -tls1 + TLSv1.1 -tls1_1 + TLSv1.2 -tls1_2 + "default" {} +} +get_hosts [lindex $argv 0] +cd $::test::dir +start_tests "TLS-соединение от клиента [lindex $argv 0]" + +set CAhost lynx.lan.cryptocom.ru +set CAprefix /cgi-bin/autoca + + +foreach alg [array names hosts] { + set alg2 [regsub {(gost\d+)cp} $alg {\1}] + set alg_fn [string map {":" "_"} $alg2] + set alg_ca [regexp -inline {^[^:]+} $alg] + log "alg_fn=$alg_fn" + if {[string match gost2001* $alg]} { + set alg_cli_list "gost2001_A gost2001_XA" + } elseif {[string match gost2012* $alg]} { + set alg_cli_list "gost2001_A gost2012_256_A gost2012_256_XA gost2012_512_A gost2012_512_B" + } else { + set alg_cli_list $alg_ca + } + + + test -skip {[file exist ca_$alg_ca.pem]} "Получить сертификат $alg_ca CA" { + getCAcert $CAhost $CAprefix $alg_ca + } 0 "ca_$alg_ca.pem" + + test -skip {[file exist srv_$alg_fn/cert.pem]} "Получить сертификат $alg для сервера" { + getCAAlgParams $CAhost $CAprefix $alg_ca + if {![makeUser srv_$alg_fn $alg2 CN [info hostname]]} { + error "Request generation failed" + } + registerUserAtCA srv_$alg_fn $CAhost $CAprefix $alg_ca + file exists srv_$alg_fn/cert.pem + } 0 1 + + if {[array exists suites]} {array unset suites} + array set suites $hosts($alg) + foreach suite [array names suites] { + if {![regexp {(.+):(.+)} $suite => proto cs]} { + set cs $suite + set proto "default" + } + if {[info exists suite_map($cs)]} { + set mycs $suite_map($cs) + } else { + set mycs $cs + } + set host [lindex [split $suites($suite) :] 0] + set host_short [lindex [split $host .] 0] + # We assume that CA certificates are already copied into Apache + # cert dir + set ca_file "/etc/apache/ssl.crt/${alg_ca}-root.crt" + + test "Корректный хэндшейк $suite" { + remote_client $host + set list [client_server [list -connect [info hostname]:4433 \ + -CAfile $ca_file -state -cipher $cs] \ + [concat [list -www -cert srv_$alg_fn/cert.pem \ + -key srv_$alg_fn/seckey.pem -cipher $mycs] $protos($proto)] {}] + set cln_exit_code [lindex $list 2] + set srv_error [string match "*error*" [lindex $list 4]] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + if {$proto == "default"} {set result_proto "default"} + list $cln_exit_code $srv_error $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 0 $proto $cs] + + + test "Сервер требует сертификат, сертификата нет $suite" { + remote_client $host + set list [client_server [list -connect [info hostname]:4433 \ + -CAfile $ca_file -state -cipher $cs] \ + [concat [list -www -cert srv_$alg_fn/cert.pem \ + -key srv_$alg_fn/seckey.pem -cipher $mycs -Verify 3 \ + -verify_return_error] $protos($proto)] {}] + string match "*error*" [lindex $list 4] + } 0 1 + + + test "Некорректный клиентский сертфиикат $suite" { + remote_client $host + set list [client_server [list -connect [info hostname]:4433 \ + -cert /home/build/client-$alg_ca/cert.pem \ + -key /home/build/client-$alg_ca/seckey.pem \ + -CAfile $ca_file -state -cipher $cs] \ + [concat [list -www -cert srv_$alg_fn/cert.pem \ + -key srv_$alg_fn/seckey.pem -cipher $mycs -Verify 3 \ + -verify_return_error] $protos($proto)] {}] + string match "*error*" [lindex $list 4] + } 0 1 + + + + foreach alg_cli $alg_cli_list { + + test "Клиентский сертификат $alg_cli $suite" { + remote_client $host + set list [client_server [list -connect [info hostname]:4433 \ + -cert /home/build/client-$alg_cli/cert.pem \ + -key /home/build/client-$alg_cli/seckey.pem \ + -CAfile $ca_file -state -cipher $cs] \ + [concat [list -www -cert srv_$alg_fn/cert.pem \ + -key srv_$alg_fn/seckey.pem -CAfile ca_$alg_ca.pem \ + -cipher $mycs -Verify 3 -verify_return_error] \ + $protos($proto)] {}] + set cln_exit_code [lindex $list 2] + set srv_error [string match "*error*" [lindex $list 4]] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + if {$proto == "default"} {set result_proto "default"} + list $cln_exit_code $srv_error $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 0 $proto $cs] + } + } +} +end_tests diff --git a/tcl_tests/cms.try b/tcl_tests/cms.try new file mode 100644 index 0000000..476da0d --- /dev/null +++ b/tcl_tests/cms.try @@ -0,0 +1,173 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на команду cms" + +test "Creating CA 2001" { + makeCA ${testname}CA gost2001:A +} 0 1 + +test "Creating CA 2012" { + makeCA +} 0 1 + + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_cms_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012 + set ca_sign_alg hash_with_sign12_512 + } + * {set ::test::ca ${testname}CA + set ca_sign_alg hash_with_sign01_cp + } + } + +test "Creating user with signing key $alg" { + makeRegisteredUser $username $alg + + if {![file exists $username/req.pem]&&[file exists $username/cert.pem]} { + file delete $username/cert.pem + } + file exists $username/cert.pem +} 0 1 + +test -skip {![file exists $username/cert.pem]} -createsfiles [list cms_sign.dat cms_sign_$alg_fn.msg] "Signing a message without cert by $alg" { + makeFile cms_sign.dat [string repeat "Test data to cms_sign.\n" 100] + openssl "cms -sign -in cms_sign.dat -text -out cms_sign_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem -nocerts" + file isfile cms_sign_$alg_fn.msg +} 0 1 + +test -skip {![file exist cms_sign_$alg_fn.msg]} "Checking micalg param in signed without cert $alg message" { + regexp -- micalg="[micalg [alg_hash $alg]]" [grep micalg [getFile cms_sign_$alg_fn.msg]] +} 0 1 + +test -createsfiles cms_sign_$alg_fn.pem -skip {![file exist cms_sign_$alg_fn.msg]} "Extracting CMS from signed without cert $alg message" { + openssl "cms -cmsout -out cms_sign_$alg_fn.pem -outform PEM -in cms_sign_$alg_fn.msg" + file isfile cms_sign_$alg_fn.pem +} 0 1 + + +test -skip {![file exists cms_sign_$alg_fn.pem]} "Checking oids in cms struct" { + extract_oids cms_sign_$alg_fn.pem PEM +} 0 [mkObjList [hash_long_name $alg] [hash_long_name $alg] "GOST R 34.11-2012 with 256 bit hash" "GOST R 34.11-2012 with 512 bit hash" "GOST R 34.11-94" "GOST 28147-89" [alg_long_name $alg]] +# hash_12_256 hash_12_512 hash_94 crypt89_cc are from sMIMECapabilities + +test -skip {![file exists cms_sign_$alg_fn.msg]} "Verifying a message signed with $alg without ca " { + grep Veri [openssl "cms -verify -text -in cms_sign_$alg_fn.msg -out cms_verified.txt -noverify -certfile $username/cert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_$alg_fn.msg]} "Verifying a message signed with $alg with ca" { + grep Veri [openssl "cms -verify -text -in cms_sign_$alg_fn.msg -out cms_verified.txt -certfile $username/cert.pem -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_$alg_fn.msg]} -createsfiles [list cms_bad_$alg_fn.msg cms_verified.txt] "Verifying corrupted messages signed with $alg" { + set corrupted [getFile cms_sign_$alg_fn.msg] + set index [string first "Test data" $corrupted ] + makeFile cms_bad_$alg_fn.msg [string replace $corrupted $index [expr $index+9] "Bad data"] + grep Verification [openssl "cms -verify -text -in cms_bad_$alg_fn.msg -out cms_verified.txt -noverify -certfile $username/cert.pem"] +} 1 "Verification failure" + +test -skip {![file exists $username/cert.pem]} -createsfiles [list cms_sign.dat cms_sign_c_$alg_fn.msg] "Signing a message by $alg with cert" { + makeFile cms_sign.dat [string repeat "Test data to sign.\n" 100] + openssl "cms -sign -in cms_sign.dat -crlfeol -text -out cms_sign_c_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem" + file isfile cms_sign_c_$alg_fn.msg +} 0 1 + +test -skip {![file exist cms_sign_c_$alg_fn.msg]} "Checking micalg param in signed with cert $alg message" { + regexp -- micalg="[micalg [alg_hash $alg]]" [grep micalg [getFile cms_sign_c_$alg_fn.msg]] +} 0 1 + +test -skip {![file exists cms_sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert inside without ca" { + grep Veri [openssl "cms -verify -text -in cms_sign_c_$alg_fn.msg -out cms_verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert with ca" { + grep Veri [openssl "cms -verify -text -in cms_sign_c_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists $username/cert.pem]} -createsfiles {cms_sign.dat cms_sign_op_$alg_fn.msg} "Signing a message by $alg with cert using opaque signing" { + makeFile cms_sign.dat [string repeat "Test data to cms_sign.\n" 100] + openssl "cms -sign -in cms_sign.dat -text -out cms_sign_op_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem -nodetach" + file isfile cms_sign_op_$alg_fn.msg +} 0 1 + +test -createsfiles cms_verified.txt -skip {![file exists cms_sign_op_$alg_fn.msg]} "Verifying a message signed by $alg having cert inside without ca" { + grep Veri [openssl "cms -verify -text -in cms_sign_op_$alg_fn.msg -out cms_verified.txt -noverify"] +} 0 "Verification successful +" + +test -createsfiles cms_verified.txt -skip {![file exists cms_sign_op_$alg_fn.msg]} "Verifying a $alg opaque message with ca" { + grep Veri [openssl "cms -verify -text -in cms_sign_op_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -createsfiles cms_broken_op_$alg_fn.msg -skip {![file exists cms_sign_op_$alg_fn.msg]} "Verifying broken $alg opaque message" { + set data [getFile cms_sign_op_$alg_fn.msg] + regexp "(.*)\n\r?\n(.+)" $data match header encoded + set asnstruct [::base64::decode $encoded] + makeFile cms_broken_op_$alg_fn.msg "$header\n\n[::base64::encode [regsub -all\ + "Test data" $asnstruct "Best data"]]" + grep Verification [openssl "cms -verify -text -in cms_broken_op_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + + +test -createsfiles "cms_sign_det_$alg_fn.msg" -skip {![file exists $username/cert.pem]||![file exists cms_sign.dat]} "Creating detached $alg signature" { + openssl "cms -sign -binary -in cms_sign.dat -out cms_sign_det_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem" + file exists cms_sign_det_$alg_fn.msg +} 0 1 + +test -skip {![file exist cms_sign_det_$alg_fn.msg]} "Checking micalg param in detached $alg signature" { + regexp -- micalg="[micalg [alg_hash $alg]]" [grep micalg [getFile cms_sign_det_$alg_fn.msg]] +} 0 1 + +test -createsfiles cms_sign_det_$alg_fn.pem -skip {![file exist cms_sign_det_$alg_fn.msg]} "Extracting CMS from signed $alg message" { +openssl "cms -cmsout -out cms_sign_det_$alg_fn.pem -outform PEM -in cms_sign_det_$alg_fn.msg" + file isfile cms_sign_$alg_fn.pem +} 0 1 + +#We expect cryptocom oids because of cert cms_signed by ca with Cryptocom algs +# Result sequence +# 1. digest +# 2. algorithm of CA key +# 3. algorithm of current key +# 4. algorithm of CA key +# 5. digest +# 6. digests from sMIMECapabilities +# 7. encryption from sMIMECapabilities +# 8. algorithm of current key +test -skip {![file exists cms_sign_$alg_fn.pem]} "Checking oids in cms struct" { + extract_oids cms_sign_det_$alg_fn.pem PEM +} 0 [mkObjList [hash_long_name $alg] [smime_hash_with_sign_long_name $ca_sign_alg] [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [smime_hash_with_sign_long_name $ca_sign_alg] [hash_long_name $alg] "GOST R 34.11-2012 with 256 bit hash" "GOST R 34.11-2012 with 512 bit hash" "GOST R 34.11-94" "GOST 28147-89" [alg_long_name $alg]] + +test -skip {![file exists cms_sign_det_$alg_fn.pem]} "Verifying detached $alg cms_signature" { + grep Veri [openssl "cms -verify -content cms_sign.dat -inform PEM -in cms_sign_det_$alg_fn.pem -out cms_verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_det_$alg_fn.msg]} -createsfiles {bad.dat} "Verifying corrupted $alg detached cms_signature" { + makeFile bad.dat [regsub Test [getFile cms_sign.dat] Best] + grep Verification [openssl "cms -verify -content bad.dat -in cms_sign_det_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + + +} +end_tests diff --git a/tcl_tests/cms2.try b/tcl_tests/cms2.try new file mode 100644 index 0000000..1233d55 --- /dev/null +++ b/tcl_tests/cms2.try @@ -0,0 +1,207 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Тесты на команду cms - вторая подпись" + +test "Creating CA" { + makeCA +} 0 1 + +makeFile cms_signed2.dat "Test data for 2 signatures" + + +foreach length {256 512} { + +test "Creating users $length" { + makeRegisteredUser U_cms_1_$length gost2012_$length:A CN USER1_$length emailAddress test@cryptocom.ru + makeRegisteredUser U_cms_2_$length gost2012_$length:A CN USER2_$length emailAddress test@cryptocom.ru +} 0 1 + +test -createsfiles cms_signed2_1_$length.asn "Signing in DER format with 1st signature" { + openssl "cms -sign -binary -outform der -inform der -nodetach -inkey U_cms_1_$length/seckey.pem -signer U_cms_1_$length/cert.pem -in cms_signed2.dat -out cms_signed2_1_$length.asn" + file isfile cms_signed2_1_$length.asn +} 0 1 + +test -createsfiles cms_signed2_2_$length.asn "Signing in DER format with 2nd signature" { + openssl "cms -resign -binary -outform der -inform der -nodetach -inkey U_cms_2_$length/seckey.pem -signer U_cms_2_$length/cert.pem -in cms_signed2_1_$length.asn -out cms_signed2_2_$length.asn" + file isfile cms_signed2_2_$length.asn +} 0 1 + +test -createsfiles {was_signed.dat signer.certs} "Verifying signature in DER format" { + grep "Verif" [openssl "cms -verify -inform der -in cms_signed2_2_$length.asn -noverify -signer signer.certs -out was_signed.dat"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile cms_signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +test -createsfiles cms_signed2_1_$length\_op.msg "Signing opaque in S/MIME format with 1st signature" { + openssl "cms -sign -binary -inform der -nodetach -inkey U_cms_1_$length/seckey.pem -signer U_cms_1_$length/cert.pem -in cms_signed2.dat -out cms_signed2_1_$length\_op.msg" + file isfile cms_signed2_1_$length\_op.msg +} 0 1 + +test -createsfiles cms_signed2_2_$length\_op.msg "Signing opaque in S/MIME format with 2nd signature" { + openssl "cms -resign -binary -nodetach -inkey U_cms_2_$length/seckey.pem -signer U_cms_2_$length/cert.pem -in cms_signed2_1_$length\_op.msg -out cms_signed2_2_$length\_op.msg" + file isfile cms_signed2_2_$length\_op.msg +} 0 1 + +test -createsfiles {was_signed.dat signer.certs} "Verifying opaque signature in S/MIME format" { + grep "Verif" [openssl "cms -verify -in cms_signed2_2_$length\_op.msg -noverify -signer signer.certs -out was_signed.dat"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile cms_signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +test -createsfiles cms_signed2_1_$length\_det.asn "Signing detached in DER format with 1st signature" { + openssl "cms -sign -binary -outform der -inkey U_cms_1_$length/seckey.pem -signer U_cms_1_$length/cert.pem -in cms_signed2.dat -out cms_signed2_1_$length\_det.asn" + file isfile cms_signed2_1_$length\_det.asn +} 0 1 + +test -createsfiles cms_signed2_2_$length\_det.asn "Signing detached in DER format with 2nd signature" { + openssl "cms -resign -binary -inkey U_cms_2_$length/seckey.pem -signer U_cms_2_$length/cert.pem -in cms_signed2_1_$length\_det.asn -content cms_signed2.dat -inform der -outform der -out cms_signed2_2_$length\_det.asn" + file isfile cms_signed2_2_$length\_det.asn +} 0 1 + +test -createsfiles {was_signed.dat signer.certs} "Verifying detached signature in DER format" { + grep "Verif" [openssl "cms -verify -in cms_signed2_2_$length\_det.asn -noverify -signer signer.certs -out was_signed.dat -content signed2.dat -inform der"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile cms_signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert_asn[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert_asn$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +test -createsfiles cms_signed2_1_$length.msg "Signing in S/MIME format with 1st signature" { + openssl "cms -sign -binary -inform der -inkey U_cms_1_$length/seckey.pem -signer U_cms_1_$length/cert.pem -in cms_signed2.dat -out cms_signed2_1_$length.msg" + file isfile cms_signed2_1_$length.msg +} 0 1 + +test -createsfiles cms_signed2_2_$length.msg "Signing in S/MIME format with 2nd signature" { + grep "SMIME" [openssl "cms -resign -binary -inkey U_cms_2_$length/seckey.pem -signer U_cms_2_$length/cert.pem -in cms_signed2_1_$length.msg -inform smime -out cms_signed2_2_$length.msg"] +} 0 "" + +test -createsfiles {was_signed.dat signer.certs} "Verifying signature in S/MIME format" { + grep "Verif" [openssl "cms -verify -in cms_signed2_2_$length.msg -noverify -signer signer.certs -out was_signed.dat -inform smime"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile cms_signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert_cms[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert_cms$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +} + + +test "Resigning in DER format with a unsuitable key length 512" { + openssl "cms -resign -binary -inform der -nodetach -inkey U_cms_2_512/seckey.pem -signer U_cms_2_512/cert.pem -in cms_signed2_1_256.asn" +} 1 "no matching digest" + +test "Resigning in DER format with a unsuitable key length 256" { + openssl "cms -resign -binary -inform der -nodetach -inkey U_cms_2_256/seckey.pem -signer U_cms_2_256/cert.pem -in cms_signed2_1_512.asn" +} 1 "no matching digest" + +test "Resigning opaque in S/MIME format with a unsuitable key length 512" { + openssl "cms -resign -binary -nodetach -inkey U_cms_2_512/seckey.pem -signer U_cms_2_512/cert.pem -in cms_signed2_1_256_op.msg" +} 1 "no matching digest" + +test "Resigning opaque in S/MIME format with a unsuitable key length 256" { + openssl "cms -resign -binary -nodetach -inkey U_cms_2_256/seckey.pem -signer U_cms_2_256/cert.pem -in cms_signed2_1_512_op.msg" +} 1 "no matching digest" + +test "Resigning detached in DER format with a unsuitable key length 512" { + openssl "cms -resign -binary -inform der -inkey U_cms_2_512/seckey.pem -signer U_cms_2_512/cert.pem -in cms_signed2_1_256_det.asn -content cms_signed2.dat" +} 1 "no matching digest" + +test "Resigning detached in DER format with a unsuitable key length 256" { + openssl "cms -resign -binary -inform der -inkey U_cms_2_256/seckey.pem -signer U_cms_2_256/cert.pem -in cms_signed2_1_512_det.asn -content cms_signed2.dat" +} 1 "no matching digest" + +test "Resigning in S/MIME format with a unsuitable key length 512" { + openssl "cms -resign -binary -inkey U_cms_2_512/seckey.pem -signer U_cms_2_512/cert.pem -in cms_signed2_1_256.msg" +} 1 "no matching digest" + +test "Resigning in S/MIME format with a unsuitable key length 256" { + openssl "cms -resign -binary -inkey U_cms_2_256/seckey.pem -signer U_cms_2_256/cert.pem -in cms_signed2_1_512.msg" +} 1 "no matching digest" + + +end_tests + + + +#./load_engine cms -sign -binary -outform der -inform der -nodetach -inkey certs/fstek.key -signer certs/fstek.crt -out cms_signed2 -in cms_signed1 +#./load_engine cms -verify -inform der -in cms_signed2 -noverify +#./load_engine cms -verify -inform der -in cms_signed2 -noverify -signer sss +#cat sss +#history +#vim sss +#./load_engine x509 -in sss sss2 +#./load_engine x509 -in sss +#./load_engine x509 -in sss -subject -noout +#./load_engine x509 -in sss2 -subject -noout +#./load_engine cms -verify -inform der -in cms_signed2 -noverify -signer sss -out qqq diff --git a/tcl_tests/cms_cs.try b/tcl_tests/cms_cs.try new file mode 100644 index 0000000..d246fe8 --- /dev/null +++ b/tcl_tests/cms_cs.try @@ -0,0 +1,126 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на интероперабельность результатов подписи командой cms с smime-проверками на эталонниках" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_cms_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca cmsCA-2012 + set ca_sign_alg hash_with_sign12_512 + } + * {set ::test::ca cmsCA + set ca_sign_alg hash_with_sign01_cp + } + } +set hosts [list tls-ref-cp21] +foreach hstname $hosts { + + +#test -skip {![file exists $username/cert.pem]} -createsfiles [list sign.dat sign_$alg_fn.msg] "Signing a message by smime without cert by $alg (verify by smime)" { +# makeFile sign.dat [string repeat "Test data to sign.\n" 100] +# openssl "cms -sign -in sign.dat -text -out sign_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem -nocerts" +# file isfile sign_$alg_fn.msg +#} 0 1 + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg without ca via smime" { + grep Veri [openssl_remote "$username sign_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/sign_$alg_fn.msg -out TESTPATH/verified.txt -noverify -certfile TESTPATH/$username/cert.pem" "cms"] +} 0 "Verification successful +" + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg with ca via smime" { + grep Veri [openssl_remote "$::test::ca sign_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/sign_$alg_fn.msg -out TESTPATH/verified.txt -certfile TESTPATH/$username/cert.pem -CAfile TESTPATH/$::test::ca/cacert.pem" "cms"] +} 0 "Verification successful +" + +test -skip {![file exists sign_$alg_fn.msg]} -createsfiles [list bad_$alg_fn.msg TESTPATH/verified.txt] "Verifying corrupted messages signed with $alg via smime" { + set corrupted [getFile sign_$alg_fn.msg] + set index [string first "Test data" $corrupted ] + makeFile bad_$alg_fn.msg [string replace $corrupted $index [expr $index+9] "Bad data"] + grep Verification [openssl_remote "bad_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/bad_$alg_fn.msg -out TESTPATH/verified.txt -noverify -certfile TESTPATH/$username/cert.pem" "cms"] +} 1 "Verification failure" + +#test -skip {![file exists $username/cert.pem]} -createsfiles [list sign.dat sign_c_$alg_fn.msg] "Signing a message by $alg with cert" { +# makeFile sign.dat [string repeat "Test data to sign.\n" 100] +# openssl "cms -sign -in sign.dat -crlfeol -text -out sign_c_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem" +# file isfile sign_c_$alg_fn.msg +#} 0 1 + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert inside without ca via smime" { + grep Veri [openssl_remote "sign_c_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/sign_c_$alg_fn.msg -out TESTPATH/verified.txt -noverify" "cms"] +} 0 "Verification successful +" + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert with ca via smime" { + grep Veri [openssl_remote "sign_c_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/sign_c_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "cms"] +} 0 "Verification successful +" + +#test -skip {![file exists $username/cert.pem]} -createsfiles {sign.dat sign_op_$alg_fn.msg} "Signing a message by $alg with cert using opaque signing" { +# makeFile sign.dat [string repeat "Test data to sign.\n" 100] +# openssl "cms -sign -in sign.dat -text -out sign_op_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem -nodetach" +# file isfile sign_op_$alg_fn.msg +#} 0 1 + +test -createsfiles TESTPATH/verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a message signed by $alg having cert inside without ca via smime" { + grep Veri [openssl_remote "sign_op_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/sign_op_$alg_fn.msg -out TESTPATH/verified.txt -noverify" "cms"] +} 0 "Verification successful +" + +test -createsfiles TESTPATH/verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a $alg opaque message with ca via smime" { + grep Veri [openssl_remote "sign_op_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/sign_op_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "cms"] +} 0 "Verification successful +" + +test -createsfiles broken_op_$alg_fn.msg -skip {![file exists sign_op_$alg_fn.msg]} "Verifying broken $alg opaque message" { + set data [getFile sign_op_$alg_fn.msg] + regexp "(.*)\n\r?\n(.+)" $data match header encoded + set asnstruct [::base64::decode $encoded] + makeFile broken_op_$alg_fn.msg "$header\n\n[::base64::encode [regsub -all\ + "Test data" $asnstruct "Best data"]]" + grep Verification [openssl_remote "broken_op_$alg_fn.msg" "$hstname" "smime -verify -text -in TESTPATH/broken_op_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "cms"] +} 1 "Verification failure" + +#test -createsfiles "sign_det_$alg_fn.msg" -skip {![file exists $username/cert.pem]||![file exists sign.dat]} "Creating detached $alg signature" { +# openssl "cms -sign -binary -in sign.dat -out sign_det_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem" +# file exists sign_det_$alg_fn.msg +#} 0 1 + +#We expect cryptocom oids because of cert signed by ca with Cryptocom algs +# Result sequence +# 1. digest +# 2. algorithm of CA key +# 3. algorithm of current key +# 4. algorithm of CA key +# 5. digest +# 6. digests from sMIMECapabilities +# 7. encryption from sMIMECapabilities +# 8. algorithm of current key + +test -skip {![file exists pk7sign_$alg_fn.pem]} "Verifying detached $alg signature via smime" { + grep Veri [openssl_remote "pk7sign_$alg_fn.pem sign.dat" "$hstname" "smime -verify -content TESTPATH/sign.dat -inform PEM -in TESTPATH/pk7sign_$alg_fn.pem -out TESTPATH/verified.txt -noverify" "cms"] +} 0 "Verification successful +" + +test -skip {![file exists pk7sign_$alg_fn.pem]} -createsfiles {bad.dat} "Verifying corrupted $alg detached signature" { + makeFile bad.dat [regsub Test [getFile sign.dat] Best] + grep Verification [openssl_remote "sign_det_$alg_fn.msg bad.dat" "$hstname" "smime -verify -content TESTPATH/bad.dat -in TESTPATH/sign_det_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "cms"] +} 1 "Verification failure" +} + +} +end_tests diff --git a/tcl_tests/cms_io.try b/tcl_tests/cms_io.try new file mode 100644 index 0000000..92c3619 --- /dev/null +++ b/tcl_tests/cms_io.try @@ -0,0 +1,81 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на совместтимость cms и smime -sign" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_cms_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca cmsCA-2012 + set ca_sign_alg hash_with_sign12_512 + } + * {set ::test::ca cmsCA + set ca_sign_alg hash_with_sign01_cp + } + } + +test -skip {![file exists cms_sign_$alg_fn.msg]} "Verifying a message signed with $alg without ca " { + grep Veri [openssl "smime -verify -text -in cms_sign_$alg_fn.msg -out cms_verified.txt -noverify -certfile $username/cert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_$alg_fn.msg]} "Verifying a message signed with $alg with ca" { + grep Veri [openssl "smime -verify -text -in cms_sign_$alg_fn.msg -out cms_verified.txt -certfile $username/cert.pem -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists cms_bad_$alg_fn.msg]} -createsfiles cms_verified.txt "Verifying corrupted messages signed with $alg" { + grep Verification [openssl "smime -verify -text -in cms_bad_$alg_fn.msg -out cms_verified.txt -noverify -certfile $username/cert.pem"] +} 1 "Verification failure" + +test -skip {![file exists cms_sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert inside without ca" { + grep Veri [openssl "smime -verify -text -in cms_sign_c_$alg_fn.msg -out cms_verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert with ca" { + grep Veri [openssl "smime -verify -text -in cms_sign_c_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -createsfiles cms_verified.txt -skip {![file exists cms_sign_op_$alg_fn.msg]} "Verifying a message signed by $alg having cert inside without ca" { + grep Veri [openssl "smime -verify -text -in cms_sign_op_$alg_fn.msg -out cms_verified.txt -noverify"] +} 0 "Verification successful +" + +test -createsfiles cms_verified.txt -skip {![file exists cms_sign_op_$alg_fn.msg]} "Verifying a $alg opaque message with ca" { + grep Veri [openssl "smime -verify -text -in cms_sign_op_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists cms_broken_op_$alg_fn.msg]} "Verifying broken $alg opaque message" { + grep Verification [openssl "smime -verify -text -in cms_broken_op_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + +test -skip {![file exists cms_sign_det_$alg_fn.msg]} "Verifying detached $alg signature" { + grep Veri [openssl "smime -verify -content cms_sign.dat -in cms_sign_det_$alg_fn.msg -out cms_verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists cms_sign_det_$alg_fn.msg]} -createsfiles {bad.dat} "Verifying corrupted $alg detached signature" { + makeFile bad.dat [regsub Test [getFile cms_sign.dat] Best] + grep Verification [openssl "smime -verify -content bad.dat -in cms_sign_det_$alg_fn.msg -out cms_verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + + +} +end_tests diff --git a/tcl_tests/cmsenc.try b/tcl_tests/cmsenc.try new file mode 100644 index 0000000..6608226 --- /dev/null +++ b/tcl_tests/cmsenc.try @@ -0,0 +1,190 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на cms -encrypt" +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} + +proc map {str list} { + set result {} + foreach a $list { + lappend result [subst $str] + } + return $result +} + +if {![file exist encrypt.dat]} { + makeFile encrypt.dat [string repeat "Test data to encrypt.\n" 100] +} + +if {![info exist env(NO_RSA)]} { +test "Creating RSA CA" { + makeCA ${testname}CA-RSA rsa:512 +} 0 1 + +foreach user {U_cms_enc_rsa_1 U_cms_enc_rsa_2} { +test "Make registered user $user" { + makeRegisteredUser $user rsa:512 CAname ${testname}CA-RSA +} 0 1 +} + + +test -createsfiles cms_enc_rsa.msg "RSA User 1 encrypts message for RSA user 2" { + openssl "cms -encrypt -in encrypt.dat -des -out cms_enc_rsa.msg U_cms_enc_rsa_2/cert.pem" + file isfile cms_enc_rsa.msg +} 0 1 + +test "RSA User 1 cannot decrypt message for RSA user 2" { + grep "Error" [openssl "cms -decrypt -in cms_enc_rsa.msg -recip U_cms_enc_rsa_1/cert.pem -inkey U_cms_enc_rsa_1/seckey.pem"] +} 1 {Error decrypting CMS} + +test -createsfiles cms_decrypt.rsa "RSA User 2 (with cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in cms_enc_rsa.msg -recip U_cms_enc_rsa_2/cert.pem -inkey U_cms_enc_rsa_2/seckey.pem -out cms_decrypt.rsa" + set result [getFile cms_decrypt.rsa] + string eq $expected $result +} 0 1 + +test -createsfiles cms_decrypt_nocert.rsa "RSA User 2 (without cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in cms_enc_rsa.msg -inkey U_cms_enc_rsa_2/seckey.pem -out cms_decrypt_nocert.rsa" + set result [getFile cms_decrypt_nocert.rsa] + string eq $expected $result +} 0 1 +} + +test "Creating CA 2001" { + makeCA ${testname}CA gost2001:A +} 0 1 + +test "Creating CA 2012" { + makeCA +} 0 1 + +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + } +} + +save_env2 {OPENSSL_CONF CRYPT_PARAMS} +makeFile cmsenc1.cnf [regsub -all "\n\\s*CRYPT_PARAMS\\s*=\[\^\n]*" [getConfig] ""] +set ::env(OPENSSL_CONF) [file join [pwd] cmsenc1.cnf] + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012} + * {set ::test::ca ${testname}CA} + } + +test "Creating user $username with key $alg" { + makeRegisteredUser $username $alg + + if {![file exists $username/req.pem]&&[file exists $username/cert.pem]} { + file delete $username/cert.pem + } + file exists $username/cert.pem +} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createsfiles cms_enc_$alg_fn.msg "Encrypting for $username" { + grep "rror" [openssl "cms -encrypt -in encrypt.dat -gost89 -out cms_enc_$alg_fn.msg U_cms_enc_$alg_fn/cert.pem"] +} 0 "" + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +test -createsfiles cms_enc_$alg_fn.pem "Extracting CMS from encrypted structure for $username" { + openssl "cms -cmsout -out cms_enc_$alg_fn.pem -outform PEM -in cms_enc_$alg_fn.msg" + file isfile cms_enc_$alg_fn.pem +} 0 1 + +test -skip {![file exists cms_enc_$alg_fn.pem]} "Checking oids in pkcs7 structure for $username" { + extract_oids cms_enc_$alg_fn.pem PEM +} 0 [mkObjList [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] "GOST 28147-89" [encr_long_name $crypt_param]] + +test -createsfiles cms_decrypt.$alg_fn "Decrypting file encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in cms_enc_$alg_fn.msg -recip U_cms_enc_$alg_fn/cert.pem -inkey U_cms_enc_$alg_fn/seckey.pem -out cms_decrypt.$alg_fn" + set result [getFile cms_decrypt.$alg_fn] + string eq $expected $result +} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createsfiles cms_enc_t_$alg_fn.msg "Encrypting for $username - text format" { + grep "rror" [openssl "cms -encrypt -text -in encrypt.dat -gost89 -out cms_enc_t_$alg_fn.msg U_cms_enc_$alg_fn/cert.pem"] +} 0 "" + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +test -createsfiles cms_decrypt_t.$alg_fn "Decrypting file text-encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -text -in cms_enc_t_$alg_fn.msg -recip U_cms_enc_$alg_fn/cert.pem -inkey U_cms_enc_$alg_fn/seckey.pem -out cms_decrypt_t.$alg_fn" + set result [getFile cms_decrypt_t.$alg_fn] + string eq $expected $result +} 0 1 + +test -createsfiles cms_decrypt_t_nocert.$alg_fn "Decrypting file text-encrypted for $username without cert" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -text -in cms_enc_t_$alg_fn.msg -inkey U_cms_enc_$alg_fn/seckey.pem -out cms_decrypt_t_nocert.$alg_fn" + set result [getFile cms_decrypt_t_nocert.$alg_fn] + string eq $expected $result +} 0 1 + +} + + +test -createfiles cms_enc_4all "Encrypt for all" { + grep "rror" [openssl "cms -encrypt -in encrypt.dat -gost89 -out cms_enc_4all.msg [map {U_cms_enc_[make_fn $a]/cert.pem} $enc_list]"] +} 0 "" + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + +test -skip {![file exists cms_enc_4all.msg]} -createsfiles cms_decrypt_4all.$alg_fn "Decrypting gost-encrypted file, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in cms_enc_4all.msg -recip $username/cert.pem -inkey $username/seckey.pem -out cms_decrypt_4all.$alg_fn" + set result [getFile cms_decrypt_4all.$alg_fn] + string eq $expected $result +} 0 1 + +test -skip {![file exists cms_enc_4all.msg]} -createsfiles cms_decrypt_4all_nocert.$alg_fn "Decrypting gost-encrypted file without cert, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in cms_enc_4all.msg -inkey $username/seckey.pem -out cms_decrypt_4all_nocert.$alg_fn" + set result [getFile cms_decrypt_4all_nocert.$alg_fn] + string eq $expected $result +} 0 1 + +} + +restore_env2 {OPENSSL_CONF CRYPT_PARAMS} + +end_tests diff --git a/tcl_tests/cmsenc_cs.try b/tcl_tests/cmsenc_cs.try new file mode 100644 index 0000000..3ae497e --- /dev/null +++ b/tcl_tests/cmsenc_cs.try @@ -0,0 +1,215 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +package require test +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на cms -encrypt с участием эталонных серверов" +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} + +proc map {str list} { + set result {} + foreach a $list { + lappend result [subst $str] + } + return $result +} + +if {![file exist encrypt.dat]} { + makeFile encrypt.dat [string repeat "Test data to encrypt.\n" 100] +} + +#if {![info exist env(/NO_RSA)]} { +#test "Creating RSA CA" { +# makeCA ${testname}CA-RSA rsa:512 +#} 0 1 +# + +set ::test::ca cmsencCA-RSA + +#foreach user {U_cms_enc_rsa_1 U_cms_enc_rsa_2} { +#test "Make registered user $user" { +# makeRegisteredUser $user rsa:512 CAname ${testname}CA-RSA +#} 0 1 +#} + +set hosts tls-ref-cp21 +foreach hstname $hosts { + +#test -createsfiles cms_enc_rsa.msg "RSA User 1 encrypts message for RSA user 2" { +# openssl "cms -encrypt -in encrypt.dat -des -out cms_enc_rsa.msg U_cms_enc_rsa_2/cert.pem" +# file isfile cms_enc_rsa.msg +#} 0 1 + +test "RSA User 1 cannot decrypt message for RSA user 2" { + grep "Error" [openssl_remote "cms_enc_rsa.msg U_cms_enc_rsa_1" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_rsa.msg -recip TESTPATH/U_cms_enc_rsa_1/cert.pem -inkey TESTPATH/U_cms_enc_rsa_1/seckey.pem" "rsa"] +} 1 {Error decrypting CMS} + +test -skip {![file exists cms_decrypt.rsa]} "RSA User 2 (with cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl_remote "U_cms_enc_rsa_2" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_rsa.msg -recip TESTPATH/U_cms_enc_rsa_2/cert.pem -inkey TESTPATH/U_cms_enc_rsa_2/seckey.pem -out TESTPATH/cms_decrypt.rsa" "rsa" + set result [getFile cms_decrypt.rsa] + string eq $expected $result +} 0 1 + +test -skip{![file exists cms_decrypt_nocert.rsa]} "RSA User 2 (without cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl_remote "$hstname" "cms -decrypt -in TESTPATH/cms_enc_rsa.msg -inkey TESTPATH/U_cms_enc_rsa_2/seckey.pem -out TESTPATH/cms_decrypt_nocert.rsa" "rsa" + set result [getFile cms_decrypt_nocert.rsa] + string eq $expected $result +} 0 1 +} + +#test "Creating CA 2001" { +# makeCA ${testname}CA gost2001:A +#} 0 1 + +#test "Creating CA 2012" { +# makeCA +#} 0 1 +# + +set hosts [list tls-ref-cp21 tls-ref-cp20] +foreach hstname $hosts { + +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} + "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} + } +} + +save_env2 {OPENSSL_CONF CRYPT_PARAMS} +makeFile cmsenc1.cnf [regsub -all "\n\\s*CRYPT_PARAMS\\s*=\[\^\n]*" [getConfig] ""] +set ::env(OPENSSL_CONF) [file join [pwd] cmsenc1.cnf] + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca cmsCA-2012} + * {set ::test::ca cmsCA} + } + +#test "Creating user $username with key $alg" { +# makeRegisteredUser $username $alg + +# if {![file exists $username/req.pem]&&[file exists $username/cert.pem]} { +# file delete $username/cert.pem +# } +# file exists $username/cert.pem +#} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +#test -createsfiles cms_enc_$alg_fn.msg "Encrypting for $username" { +# grep "rror" [openssl "cms -encrypt -in encrypt.dat -gost89 -out cms_enc_$alg_fn.msg U_cms_enc_$alg_fn/cert.pem"] +#} 0 "" + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +#test -createsfiles cms_enc_$alg_fn.pem "Extracting CMS from encrypted structure for $username" { +# openssl "cms -cmsout -out cms_enc_$alg_fn.pem -outform PEM -in cms_enc_$alg_fn.msg" +# file isfile cms_enc_$alg_fn.pem +#} 0 1 + +#test -skip {![file exists cms_enc_$alg_fn.pem]} "Checking oids in pkcs7 structure for $username" { +# extract_oids cms_enc_$alg_fn.pem PEM +#} 0 [mkObjList [alg_id $alg] [param_pubkey $alg] [param_hash $alg] crypt89_cc [param_encr $crypt_param]] + +#test "Decrypting file encrypted for $username" { +# set expected [getFile encrypt.dat] +# openssl_remote "$username U_cms_enc_$alg_fn cms_enc_$alg_fn.msg" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_$alg_fn.msg -recip TESTPATH/U_cms_enc_$alg_fn/cert.pem -inkey TESTPATH/U_cms_enc_$alg_fn/seckey.pem -out TESTPATH/cms_decrypt.$alg_fn" "$testname" +# set result [getRemoteFile "$hstname" "TESTPATH/cms_decrypt.$alg_fn" "$testname"] +# string eq $expected $result +#} 0 1 + +test "Decrypting file encrypted for $username" { + set expected [getFile encrypt.dat] + set result [openssl_remote "$username U_cms_enc_$alg_fn cms_enc_$alg_fn.msg" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_$alg_fn.msg -recip TESTPATH/U_cms_enc_$alg_fn/cert.pem -inkey TESTPATH/U_cms_enc_$alg_fn/seckey.pem" "$testname"] + string eq $expected $result +} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +#test -createsfiles cms_enc_t_$alg_fn.msg "Encrypting for $username - text format" { +# grep "rror" [openssl "cms -encrypt -text -in encrypt.dat -gost89 -out cms_enc_t_$alg_fn.msg U_cms_enc_$alg_fn/cert.pem"] +#} 0 "" + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +test -createsfiles cms_decrypt_t.$alg_fn "Decrypting file text-encrypted for $username" { + set expected [getFile encrypt.dat] + set result [openssl_remote "cms_enc_t_$alg_fn.msg U_cms_enc_$alg_fn" "$hstname" "cms -decrypt -text -in TESTPATH/cms_enc_t_$alg_fn.msg -recip TESTPATH/U_cms_enc_$alg_fn/cert.pem -inkey TESTPATH/U_cms_enc_$alg_fn/seckey.pem" "$testname"] + string eq $expected $result +} 0 1 + +test -createsfiles cms_decrypt_t_nocert.$alg_fn "Decrypting file text-encrypted for $username without cert" { + set expected [getFile encrypt.dat] + set result [openssl_remote "cms_enc_t_$alg_fn.msg U_cms_enc_$alg_fn" "$hstname" "cms -decrypt -text -in TESTPATH/cms_enc_t_$alg_fn.msg -inkey TESTPATH/U_cms_enc_$alg_fn/seckey.pem" "$testname"] + string eq $expected $result +} 0 1 + +} + + +#test -createfiles cms_enc_4all_old "Encrypt for all" { +# puts stdout $enc_list +# grep "rror" [openssl "cms -encrypt -in encrypt.dat -gost89 -out cms_enc_4all_old.msg [map {U_cms_enc_[make_fn $a]/cert.pem} $enc_list]"] +#} 0 "" + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createfiles cms_enc_4all_old "Encrypt for all" { + grep "rror" [openssl "cms -encrypt -in encrypt.dat -gost89 -out cms_enc_4all_old.msg [map {U_cms_enc_[make_fn $a]/cert.pem} $enc_list]"] +} 0 "" + +test -skip {![file exists cms_enc_4all_old.msg]} "Decrypting gost-encrypted file, recipient $alg_fn" { + set expected [getFile encrypt.dat] + set result [openssl_remote "cms_enc_4all_old.msg $username" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_4all_old.msg -recip TESTPATH/$username/cert.pem -inkey TESTPATH/$username/seckey.pem" "$testname"] + string eq $expected $result +} 0 1 + +test -skip {![file exists cms_enc_4all_old.msg]} -createsfiles cms_decrypt_4all_nocert.$alg_fn "Decrypting gost-encrypted file without cert, recipient $alg_fn" { + set expected [getFile encrypt.dat] + set result [openssl_remote "cms_enc_4all_old.msg $username" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_4all_old.msg -inkey TESTPATH/$username/seckey.pem" "$testname"] + string eq $expected $result +} 0 1 + +} + +restore_env2 {OPENSSL_CONF CRYPT_PARAMS} +} + + +end_tests diff --git a/tcl_tests/cmsenc_io.try b/tcl_tests/cmsenc_io.try new file mode 100644 index 0000000..a78e113 --- /dev/null +++ b/tcl_tests/cmsenc_io.try @@ -0,0 +1,108 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на совместимость cms и smime -encrypt" +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} + +proc map {str list} { + set result {} + foreach a $list { + lappend result [subst $str] + } + return $result +} + +if {![info exist env(NO_RSA)]} { + +test -createsfiles io_cms_decrypt.rsa "RSA User 2 (with cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in cms_enc_rsa.msg -recip U_cms_enc_rsa_2/cert.pem -inkey U_cms_enc_rsa_2/seckey.pem -out io_cms_decrypt.rsa" + set result [getFile io_cms_decrypt.rsa] + string eq $expected $result +} 0 1 + +test -createsfiles io_cms_decrypt_nocert.rsa "RSA User 2 (without cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in cms_enc_rsa.msg -inkey U_cms_enc_rsa_2/seckey.pem -out io_cms_decrypt_nocert.rsa" + set result [getFile io_cms_decrypt_nocert.rsa] + string eq $expected $result +} 0 1 +} + + +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + } +} + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012} + * {set ::test::ca ${testname}CA} + } + +test -createsfiles io_cms_decrypt.$alg_fn "Decrypting file encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in cms_enc_$alg_fn.msg -recip U_cms_enc_$alg_fn/cert.pem -inkey U_cms_enc_$alg_fn/seckey.pem -out io_cms_decrypt.$alg_fn" + set result [getFile io_cms_decrypt.$alg_fn] + string eq $expected $result +} 0 1 + +test -createsfiles io_cms_decrypt_t.$alg_fn "Decrypting file text-encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -text -in cms_enc_t_$alg_fn.msg -recip U_cms_enc_$alg_fn/cert.pem -inkey U_cms_enc_$alg_fn/seckey.pem -out io_cms_decrypt_t.$alg_fn" + set result [getFile io_cms_decrypt_t.$alg_fn] + string eq $expected $result +} 0 1 + +test -createsfiles io_cms_decrypt_t_nocert.$alg_fn "Decrypting file text-encrypted for $username without cert" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -text -in cms_enc_t_$alg_fn.msg -inkey U_cms_enc_$alg_fn/seckey.pem -out io_cms_decrypt_t_nocert.$alg_fn" + set result [getFile io_cms_decrypt_t_nocert.$alg_fn] + string eq $expected $result +} 0 1 + +} + + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + +test -skip {![file exists cms_enc_4all.msg]} -createsfiles io_cms_decrypt_4all.$alg_fn "Decrypting gost-encrypted file, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in cms_enc_4all.msg -recip $username/cert.pem -inkey $username/seckey.pem -out io_cms_decrypt_4all.$alg_fn" + set result [getFile io_cms_decrypt_4all.$alg_fn] + string eq $expected $result +} 0 1 + +test -skip {![file exists cms_enc_4all.msg]} -createsfiles io_cms_decrypt_4all_nocert.$alg_fn "Decrypting gost-encrypted file without cert, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in cms_enc_4all.msg -inkey $username/seckey.pem -out io_cms_decrypt_4all_nocert.$alg_fn" + set result [getFile io_cms_decrypt_4all_nocert.$alg_fn] + string eq $expected $result +} 0 1 + +} + +end_tests diff --git a/tcl_tests/cmsenc_sc.try b/tcl_tests/cmsenc_sc.try new file mode 100644 index 0000000..dbd4c2b --- /dev/null +++ b/tcl_tests/cmsenc_sc.try @@ -0,0 +1,191 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +package require test +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на cms -encrypt с участием эталонных серверов (шифрование на эталонниках)" +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} + +proc map {str list} { + set result {} + foreach a $list { + lappend result [subst $str] + } + return $result +} + +if {![file exist encrypt.dat]} { + makeFile encrypt.dat [string repeat "Test data to encrypt.\n" 100] +} + +#if {![info exist env(/NO_RSA)]} { +#test "Creating RSA CA" { +# makeCA ${testname}CA-RSA rsa:512 +#} 0 1 +# + +#set ::test::ca cmsencCA-RSA + +#foreach user {U_cms_enc_rsa_1 U_cms_enc_rsa_2} { +#test "Make registered user $user" { +# makeRegisteredUser $user rsa:512 CAname ${testname}CA-RSA +#} 0 1 +#} + +#set hosts tls-ref-cp21 +#foreach hstname $hosts { + +#test -createsfiles cms_enc_rsa.msg "RSA User 1 encrypts message for RSA user 2" { +# openssl "cms -encrypt -in encrypt.dat -des -out cms_enc_rsa.msg U_cms_enc_rsa_2/cert.pem" +# file isfile cms_enc_rsa.msg +#} 0 1 + +#test "RSA User 1 cannot decrypt message for RSA user 2" { +# grep "Error" [openssl_remote "cms_enc_rsa.msg U_cms_enc_rsa_1" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_rsa.msg -recip TESTPATH/U_cms_enc_rsa_1/cert.pem -inkey TESTPATH/U_cms_enc_rsa_1/seckey.pem" "rsa"] +#} 1 {Error decrypting CMS} + +#test -skip {![file exists cms_decrypt.rsa]} "RSA User 2 (with cert) can decrypt message for RSA user 2" { +# set expected [getFile encrypt.dat] +# openssl_remote "U_cms_enc_rsa_2" "$hstname" "cms -decrypt -in TESTPATH/cms_enc_rsa.msg -recip TESTPATH/U_cms_enc_rsa_2/cert.pem -inkey TESTPATH/U_cms_enc_rsa_1/seckey.pem -out TESTPATH/cms_decrypt.rsa" "rsa" +# set result [getFile cms_decrypt.rsa] +# string eq $expected $result +#} 0 1 + +#test -skip{![file exists cms_decrypt_nocert.rsa]} "RSA User 2 (without cert) can decrypt message for RSA user 2" { +# set expected [getFile encrypt.dat] +# openssl_remote "$hstname" "cms -decrypt -in TESTPATH/cms_enc_rsa.msg -inkey TESTPATH/U_cms_enc_rsa_2/seckey.pem -out TESTPATH/cms_decrypt_nocert.rsa" "rsa" +# set result [getFile cms_decrypt_nocert.rsa] +# string eq $expected $result +#} 0 1 +#} + +#test "Creating CA 2001" { +# makeCA ${testname}CA gost2001:A +#} 0 1 + +#test "Creating CA 2012" { +# makeCA +#} 0 1 +# + +set hosts [list tls-ref-cp21 tls-ref-cp20] +foreach hstname $hosts { + +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} + "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 }} + } +} + +save_env2 {OPENSSL_CONF CRYPT_PARAMS} +makeFile cmsenc1.cnf [regsub -all "\n\\s*CRYPT_PARAMS\\s*=\[\^\n]*" [getConfig] ""] +set ::env(OPENSSL_CONF) [file join [pwd] cmsenc1.cnf] + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca cmsencCA-2012} + * {set ::test::ca cmsencCA} + } + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createsfiles cms_enc_sc_$alg_fn.msg "Encrypting for $username" { + file mkdir 1_$hstname/$alg_fn + set res [open 1_$hstname/$alg_fn/cms_enc_sc_$alg_fn.msg w] + puts $res [openssl_remote "encrypt.dat $username $::test::ca" "$hstname" "cms -encrypt -in TESTPATH/encrypt.dat -gost89 TESTPATH/$username/cert.pem" "$testname"] + close $res +} 0 "" + +#if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +test "Decrypting file encrypted for $username" { + set expected [getFile encrypt.dat] + set result [openssl "cms -decrypt -in 1_$hstname/$alg_fn/cms_enc_sc_$alg_fn.msg -recip $username/cert.pem -inkey $username/seckey.pem"] + string eq $expected $result +} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createsfiles cms_enc_t_$alg_fn.msg "Encrypting for $username - text format" { + file mkdir 2_$hstname/$alg_fn + set res [open 2_$hstname/$alg_fn/cms_enc_sc_t_$alg_fn.msg w] + puts $res [openssl_remote "encrypt.dat $username $::test::ca" "$hstname" "cms -encrypt -text -in TESTPATH/encrypt.dat -gost89 TESTPATH/$username/cert.pem" "$testname"] + close $res +} 0 "" + +#if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + + + +test "Decrypting file text-encrypted for $username" { + set expected [getFile encrypt.dat] + set result [openssl "cms -decrypt -text -in 2_$hstname/$alg_fn/cms_enc_sc_t_$alg_fn.msg -recip $username/cert.pem -inkey $username/seckey.pem"] + string eq $expected $result +} 0 1 + +test "Decrypting file text-encrypted for $username without cert" { + set expected [getFile encrypt.dat] + set result [openssl "cms -decrypt -text -in 2_$hstname/$alg_fn/cms_enc_sc_t_$alg_fn.msg -inkey $username/seckey.pem"] + string eq $expected $result +} 0 1 + +} + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_cms_enc_$alg_fn + +# Не мапится $a, потому отваливаются тесты. Выяснить, почему +test "Encrypt for all" { + file mkdir 3_$hstname/$alg_fn + set res [open 3_$hstname/$alg_fn/cms_enc_4all_old.msg w] + puts $res [openssl_remote "encrypt.dat $username $::test::ca" "$hstname" "cms -encrypt -in TESTPATH/encrypt.dat -gost89 [map {TESTPATH/U_cms_enc_[make_fn $a]/cert.pem} $enc_list]" "$testname"] + close $res +} 0 "" + +test -skip {![file exists cms_enc_4all_old.msg]} "Decrypting gost-encrypted file, recipient $alg_fn" { + set expected [getFile encrypt.dat] + set result [openssl "cms -decrypt -in 3_$hstname/$alg_fn/cms_enc_4all_old.msg -recip $username/cert.pem -inkey $username/seckey.pem"] + string eq $expected $result +} 0 1 + +test -skip {![file exists cms_enc_4all_old.msg]} -createsfiles cms_decrypt_4all_nocert.$alg_fn "Decrypting gost-encrypted file without cert, recipient $alg_fn" { + set expected [getFile encrypt.dat] + set result [openssl "cms -decrypt -in 3_$hstname/$alg_fn/cms_enc_4all_old.msg -inkey $username/seckey.pem"] + string eq $expected $result +} 0 1 + +} + +restore_env2 {OPENSSL_CONF CRYPT_PARAMS} +} + + + +end_tests diff --git a/tcl_tests/cmstc262019.try b/tcl_tests/cmstc262019.try new file mode 100644 index 0000000..478298e --- /dev/null +++ b/tcl_tests/cmstc262019.try @@ -0,0 +1,34 @@ +#!/usr/bin/tclsh + +lappend auto_path [file dirname [info script]] +package require ossltest +file delete -force $::test::dir/tc26_cms +file copy -force tc26_cms $::test::dir +cd $::test::dir + +start_tests "CMS tests, TC26 examples" + +#BUILD_AT=obj_mid.l64/ ./openssl_wrap.sh cms -verify -in ../standalone-test/tc26_cms/signed_a111.pem -inform PEM -noverify +#BUILD_AT=obj_mid.l64/ ./openssl_wrap.sh cms -verify -in ../standalone-test/tc26_cms/signed_a121.pem -inform PEM -noverify + +test "Signed data, 512 bits, signed attributes" { + grep "Verification successful" [openssl "cms -verify -in tc26_cms/signed_a111.pem -inform PEM -noverify"] +} 0 "Verification successful +" + +test "Signed data, 256 bits, no signed attributes" { + grep "Verification successful" [openssl "cms -verify -in tc26_cms/signed_a121.pem -inform PEM -noverify"] +} 0 "Verification successful +" + +test "Digested data, 256 bits" { + grep "Verification successful" [openssl "cms -digest_verify -in tc26_cms/hashed_a311.pem -inform PEM -out hashed_a311.out"] +} 0 "Verification successful +" + +test "Digested data, 512 bits" { + grep "Verification successful" [openssl "cms -digest_verify -in tc26_cms/hashed_a321.pem -inform PEM -out hashed_a321.out"] +} 0 "Verification successful +" + +end_tests diff --git a/tcl_tests/cnt0.enc b/tcl_tests/cnt0.enc new file mode 100644 index 0000000..a9dd2d2 --- /dev/null +++ b/tcl_tests/cnt0.enc @@ -0,0 +1 @@ +Salted__h§®› 5UÏå,ž~j³ˆD±¤ug¼]_' \ No newline at end of file diff --git a/tcl_tests/cnt1.enc b/tcl_tests/cnt1.enc new file mode 100644 index 0000000..71551f3 --- /dev/null +++ b/tcl_tests/cnt1.enc @@ -0,0 +1 @@ +å,ž~j³ˆD±¤ug¼]_' \ No newline at end of file diff --git a/tcl_tests/cp10.ciphers b/tcl_tests/cp10.ciphers new file mode 100644 index 0000000..7e4fe75 --- /dev/null +++ b/tcl_tests/cp10.ciphers @@ -0,0 +1,22 @@ +# +# óÔÒÕËÔÕÒÁ ÆÁÊÌÁ +# ÁÌÇÏÒÉÔÍ { +# ciphersuite host:port[:servertype] +# ciphersuite host:port +# +# } +# Where servertype can be apache (default) or iis. + +#gost94:XA { +# GOST94-GOST89-GOST89 tls-ref-cp10.lan.cryptocom.ru:4403 +# GOST94-NULL-GOST94 tls-ref-cp10.lan.cryptocom.ru:4404 +#} + +gost2001:XA { + GOST2001-GOST89-GOST89 tls-ref-cp10.lan.cryptocom.ru:443 + GOST2001-NULL-GOST94 tls-ref-cp10.lan.cryptocom.ru:4401 +} +rsa:1024 { + DHE-RSA-AES256-SHA tls-ref-cp10.lan.cryptocom.ru:4407 + RC4-SHA tls-ref-cp10.lan.cryptocom.ru:4408 +} diff --git a/tcl_tests/cp20.ciphers b/tcl_tests/cp20.ciphers new file mode 100644 index 0000000..2b4bf78 --- /dev/null +++ b/tcl_tests/cp20.ciphers @@ -0,0 +1,8 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 tls-ref-cp20.lan.cryptocom.ru:443 + GOST2001-NULL-GOST94 tls-ref-cp20.lan.cryptocom.ru:4401 +} +rsa:1024 { + DHE-RSA-AES256-SHA tls-ref-cp20.lan.cryptocom.ru:4407 + RC4-SHA tls-ref-cp20.lan.cryptocom.ru:4408 +} diff --git a/tcl_tests/cp21.ciphers b/tcl_tests/cp21.ciphers new file mode 100644 index 0000000..914c2d1 --- /dev/null +++ b/tcl_tests/cp21.ciphers @@ -0,0 +1,17 @@ +# +# óÔÒÕËÔÕÒÁ ÆÁÊÌÁ +# ÁÌÇÏÒÉÔÍ { +# ciphersuite host:port[:servertype] +# ciphersuite host:port +# +# } +# Where servertype can be apache (default) or iis. + +gost2001:XA { + TLSv1:GOST2001-GOST89-GOST89 tls-ref-cp21.lan.cryptocom.ru:443 + TLSv1:GOST2001-NULL-GOST94 tls-ref-cp21.lan.cryptocom.ru:4401 +} +rsa:1024 { + TLSv1.2:DHE-RSA-AES256-SHA tls-ref-cp21.lan.cryptocom.ru:4407 + TLSv1.2:RC4-SHA tls-ref-cp21.lan.cryptocom.ru:4408 +} diff --git a/tcl_tests/csp3.ciphers b/tcl_tests/csp3.ciphers new file mode 100644 index 0000000..77d2837 --- /dev/null +++ b/tcl_tests/csp3.ciphers @@ -0,0 +1,4 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp3-srv.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/csp36.ciphers b/tcl_tests/csp36.ciphers new file mode 100644 index 0000000..a791e00 --- /dev/null +++ b/tcl_tests/csp36.ciphers @@ -0,0 +1,4 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp36-srv.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/csp36r2.ciphers b/tcl_tests/csp36r2.ciphers new file mode 100644 index 0000000..a187bfa --- /dev/null +++ b/tcl_tests/csp36r2.ciphers @@ -0,0 +1,4 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp36r2-srv.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/csp36r3.ciphers b/tcl_tests/csp36r3.ciphers new file mode 100644 index 0000000..8ea8cd5 --- /dev/null +++ b/tcl_tests/csp36r3.ciphers @@ -0,0 +1,4 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp36r3-srv.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/csp36r4.ciphers b/tcl_tests/csp36r4.ciphers new file mode 100644 index 0000000..2ef5496 --- /dev/null +++ b/tcl_tests/csp36r4.ciphers @@ -0,0 +1,4 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp36r4-srv.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/csp39.ciphers b/tcl_tests/csp39.ciphers new file mode 100644 index 0000000..82bd0ba --- /dev/null +++ b/tcl_tests/csp39.ciphers @@ -0,0 +1,4 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp39-srv.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/csp4.ciphers b/tcl_tests/csp4.ciphers new file mode 100644 index 0000000..55b5e8f --- /dev/null +++ b/tcl_tests/csp4.ciphers @@ -0,0 +1,10 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp4-01.vm.cryptocom.ru:443:iis + GOST2012-GOST8912-GOST8912 v-cp4-01.vm.cryptocom.ru:443:iis +} +gost2012_256:XA { + GOST2012-GOST8912-GOST8912 v-cp4-12S.vm.cryptocom.ru:443:iis +} +gost2012_512:A { + GOST2012-GOST8912-GOST8912 v-cp4-12L.vm.cryptocom.ru:443:iis +} diff --git a/tcl_tests/csp4r2.ciphers b/tcl_tests/csp4r2.ciphers new file mode 100644 index 0000000..44eb401 --- /dev/null +++ b/tcl_tests/csp4r2.ciphers @@ -0,0 +1,10 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp4r2-01.vm.cryptocom.ru:443:iis + GOST2012-GOST8912-GOST8912 v-cp4r2-01.vm.cryptocom.ru:443:iis +} +gost2012_256:XA { + GOST2012-GOST8912-GOST8912 v-cp4r2-12S.vm.cryptocom.ru:443:iis +} +gost2012_512:A { + GOST2012-GOST8912-GOST8912 v-cp4r2-12L.vm.cryptocom.ru:443:iis +} diff --git a/tcl_tests/csp4r3.ciphers b/tcl_tests/csp4r3.ciphers new file mode 100644 index 0000000..acedd4f --- /dev/null +++ b/tcl_tests/csp4r3.ciphers @@ -0,0 +1,14 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp4r3-01.vm.cryptocom.ru:443:iis + GOST2012-GOST8912-GOST8912 v-cp4r3-01.vm.cryptocom.ru:443:iis +} +gost2012_256:XA { + TLSv1:GOST2012-GOST8912-GOST8912 v-cp4r3-12S.vm.cryptocom.ru:443:iis + TLSv1.1:GOST2012-GOST8912-GOST8912 v-cp4r3-12S.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-GOST8912-GOST8912 v-cp4r3-12S.vm.cryptocom.ru:443:iis +} +gost2012_512:A { + TLSv1:GOST2012-GOST8912-GOST8912 v-cp4r3-12L.vm.cryptocom.ru:443:iis + TLSv1.1:GOST2012-GOST8912-GOST8912 v-cp4r3-12L.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-GOST8912-GOST8912 v-cp4r3-12L.vm.cryptocom.ru:443:iis +} diff --git a/tcl_tests/csp5.ciphers b/tcl_tests/csp5.ciphers new file mode 100644 index 0000000..e8c1a45 --- /dev/null +++ b/tcl_tests/csp5.ciphers @@ -0,0 +1,18 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-cp5-01.vm.cryptocom.ru:443:iis + GOST2012-GOST8912-GOST8912 v-cp5-01.vm.cryptocom.ru:443:iis +} +gost2012_256:XA { + TLSv1:GOST2012-GOST8912-GOST8912 v-cp5-12S.vm.cryptocom.ru:443:iis + TLSv1.1:GOST2012-GOST8912-GOST8912 v-cp5-12S.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-GOST8912-GOST8912 v-cp5-12S.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-KUZNYECHIK-KUZNYECHIKOMAC v-cp5-12S.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-MAGMA-MAGMAOMAC v-cp5-12S.vm.cryptocom.ru:443:iis +} +gost2012_512:A { + TLSv1:GOST2012-GOST8912-GOST8912 v-cp5-12L.vm.cryptocom.ru:443:iis + TLSv1.1:GOST2012-GOST8912-GOST8912 v-cp5-12L.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-GOST8912-GOST8912 v-cp5-12L.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-KUZNYECHIK-KUZNYECHIKOMAC v-cp5-12L.vm.cryptocom.ru:443:iis + TLSv1.2:GOST2012-MAGMA-MAGMAOMAC v-cp5-12L.vm.cryptocom.ru:443:iis +} diff --git a/tcl_tests/dgst.try b/tcl_tests/dgst.try new file mode 100644 index 0000000..148e1d6 --- /dev/null +++ b/tcl_tests/dgst.try @@ -0,0 +1,126 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Тесты на команду dgst" + +switch -exact [engine_name] { + "ccore" {set signalg { gost2001:A gost2012_256:A gost2012_512:A}} + "open" {set signalg { gost2001:A gost2012_256:A gost2012_512:A}} +} + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + set alg_list $signalg +} + +set hash_alg_list { md5 hash_94 hash_12_256 hash_12_512 } + +# =============== +# GOST 34.11-2012 +# =============== + +test -createsfiles {dgst.dat dgst0.dat dgst2.dat dgst8.dat dgst63.dat dgst_CF.dat} "Формирование тестовых данных" { + makeFile dgst.dat [string repeat "Test data to digest.\n" 100] binary + makeFile dgst0.dat "" binary + makeFile dgst63.dat "012345678901234567890123456789012345678901234567890123456789012" binary + file copy -force ../dgst_CF.dat ../dgst_ex1.dat ../dgst_ex2.dat . +} 0 "" + +test "Вычисление дайджеста алгоритмом md_gost12_256" { + grep "md_gost12_256\\(" [openssl "dgst -md_gost12_256 dgst63.dat"] +} 0 "md_gost12_256\(dgst63.dat)= 9d151eefd8590b89daa6ba6cb74af9275dd051026bb149a452fd84e5e57b5500\n" + +test "Два дайджеста алгоритмом md_gost12_256" { + grep "md_gost12_256\\(" [openssl "dgst -md_gost12_256 dgst63.dat dgst63.dat"] +} 0 "md_gost12_256\(dgst63.dat)= 9d151eefd8590b89daa6ba6cb74af9275dd051026bb149a452fd84e5e57b5500\nmd_gost12_256\(dgst63.dat)= 9d151eefd8590b89daa6ba6cb74af9275dd051026bb149a452fd84e5e57b5500\n" + +test "Дайджест от данных нулевой длины алгоритмом md_gost12_256" { + grep "md_gost12_256\\(" [openssl "dgst -md_gost12_256 dgst0.dat"] +} 0 "md_gost12_256\(dgst0.dat)= 3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb\n" + +test "Дайджест от спец.данных алгоритмом md_gost12_256" { + grep "md_gost12_256\\(" [openssl "dgst -md_gost12_256 dgst_CF.dat"] +} 0 "md_gost12_256\(dgst_CF.dat)= 81bb632fa31fcc38b4c379a662dbc58b9bed83f50d3a1b2ce7271ab02d25babb\n" + +test "Дайджест от контрольного примера 1 алгоритмом md_gost12_256" { + grep "md_gost12_256\\(" [openssl "dgst -md_gost12_256 dgst_ex1.dat"] +} 0 "md_gost12_256\(dgst_ex1.dat)= 9d151eefd8590b89daa6ba6cb74af9275dd051026bb149a452fd84e5e57b5500\n" + +test "Дайджест от контрольного примера 2 алгоритмом md_gost12_256" { + grep "md_gost12_256\\(" [openssl "dgst -md_gost12_256 dgst_ex2.dat"] +} 0 "md_gost12_256\(dgst_ex2.dat)= 9dd2fe4e90409e5da87f53976d7405b0c0cac628fc669a741d50063c557e8f50\n" + +test "Вычисление дайджеста алгоритмом md_gost12_512" { + grep "md_gost12_512\\(" [openssl "dgst -md_gost12_512 dgst63.dat"] +} 0 "md_gost12_512\(dgst63.dat)= 1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48\n" + +test "Два дайджеста алгоритмом md_gost12_512" { + grep "md_gost12_512\\(" [openssl "dgst -md_gost12_512 dgst63.dat dgst63.dat"] +} 0 "md_gost12_512\(dgst63.dat)= 1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48\nmd_gost12_512\(dgst63.dat)= 1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48\n" + +test "Дайджест от данных нулевой длины алгоритмом md_gost12_512" { + grep "md_gost12_512\\(" [openssl "dgst -md_gost12_512 dgst0.dat"] +} 0 "md_gost12_512\(dgst0.dat)= 8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a\n" + +test "Дайджест от спец.данных алгоритмом md_gost12_512" { + grep "md_gost12_512\\(" [openssl "dgst -md_gost12_512 dgst_CF.dat"] +} 0 "md_gost12_512\(dgst_CF.dat)= 8b06f41e59907d9636e892caf5942fcdfb71fa31169a5e70f0edb873664df41c2cce6e06dc6755d15a61cdeb92bd607cc4aaca6732bf3568a23a210dd520fd41\n" + +test "Дайджест от контрольного примера 1 алгоритмом md_gost12_512" { + grep "md_gost12_512\\(" [openssl "dgst -md_gost12_512 dgst_ex1.dat"] +} 0 "md_gost12_512\(dgst_ex1.dat)= 1b54d01a4af5b9d5cc3d86d68d285462b19abc2475222f35c085122be4ba1ffa00ad30f8767b3a82384c6574f024c311e2a481332b08ef7f41797891c1646f48\n" + +test "Дайджест от контрольного примера 2 алгоритмом md_gost12_512" { + grep "md_gost12_512\\(" [openssl "dgst -md_gost12_512 dgst_ex2.dat"] +} 0 "md_gost12_512\(dgst_ex2.dat)= 1e88e62226bfca6f9994f1f2d51569e0daf8475a3b0fe61a5300eee46d961376035fe83549ada2b8620fcd7c496ce5b33f0cb9dddc2b6460143b03dabac9fb28\n" + +test "Вычисление дайджеста алгоритмом md_gost94" { + grep "md_gost94\\(" [openssl "dgst -md_gost94 dgst.dat"] +} 0 "md_gost94\(dgst.dat)= 42e462ce1c2b4bf72a4815b7b4877c601f05e5781a71eaa36f63f836c021865c\n" + +test "Вычисление двух дайджестов алгоритмом md_gost94" { + grep "md_gost94\\(" [openssl "dgst -md_gost94 dgst.dat dgst.dat"] +} 0 "md_gost94\(dgst.dat)= 42e462ce1c2b4bf72a4815b7b4877c601f05e5781a71eaa36f63f836c021865c\nmd_gost94\(dgst.dat)= 42e462ce1c2b4bf72a4815b7b4877c601f05e5781a71eaa36f63f836c021865c\n" + +test "Вычисление дайджеста от данных нулевой длины алгоритмом md_gost94" { + grep "md_gost94\\(" [openssl "dgst -md_gost94 dgst0.dat"] +} 0 "md_gost94\(dgst0.dat)= 3f25bc1fbbce27ca10fb1958f319473ae7e17482c3b53ecf47a7e2de8aabe4c8\n" + + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username dgst_$alg_fn + + test -createsfiles $username/seckey.pem "Секретный ключ, алгоритм $alg" { + makeSecretKey $username $alg + } 0 1 + + + foreach hash_alg $hash_alg_list { + + if { ($alg eq "rsa:") || ($hash_alg eq [alg_hash $alg]) } { + + test -skip {![file exists $username/seckey.pem]||![file exists dgst.dat]} -createsfiles $username/sig.bin "Подпись дайджеста $hash_alg алгоритмом $alg" { + openssl "dgst -[hash_short_name $hash_alg] -sign $username/seckey.pem -out $username/sig.bin dgst.dat" + expr {[file size $username/sig.bin] > 0} + } 0 1 + + test -skip {![file exists $username/seckey.pem]||![file exists $username/sig.bin]} "Проверка подписи под дайджестом $hash_alg на алгоритме $alg" { + grep Verif [openssl "dgst -[hash_short_name $hash_alg] -prverify $username/seckey.pem -signature $username/sig.bin dgst.dat"] + } 0 "Verified OK +" + + } else { + test -skip {![file exists $username/seckey.pem]||![file exists dgst.dat]} -createsfiles "testmd5.bin" "Подпись несовместимого дайджеста $hash_alg c подписью $alg" { + grep Error [openssl "dgst -[hash_short_name $hash_alg] -sign $username/seckey.pem -out testmd5.bin dgst.dat"] + } 1 {invalid digest type} + } + + } + +} + +end_tests diff --git a/tcl_tests/dgst_CF.dat b/tcl_tests/dgst_CF.dat new file mode 100644 index 0000000..a6c1240 --- /dev/null +++ b/tcl_tests/dgst_CF.dat @@ -0,0 +1 @@ +îîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîîî \ No newline at end of file diff --git a/tcl_tests/dgst_ex1.dat b/tcl_tests/dgst_ex1.dat new file mode 100644 index 0000000..f2316d6 --- /dev/null +++ b/tcl_tests/dgst_ex1.dat @@ -0,0 +1 @@ +012345678901234567890123456789012345678901234567890123456789012 \ No newline at end of file diff --git a/tcl_tests/dgst_ex2.dat b/tcl_tests/dgst_ex2.dat new file mode 100644 index 0000000..57f9b3f --- /dev/null +++ b/tcl_tests/dgst_ex2.dat @@ -0,0 +1 @@ +Ñå âåòðè, Ñòðèáîæè âíóöè, âåþòú ñ ìîðÿ ñòðåëàìè íà õðàáðûÿ ïëúêû Èãîðåâû \ No newline at end of file diff --git a/tcl_tests/enc.try b/tcl_tests/enc.try new file mode 100644 index 0000000..2f682a7 --- /dev/null +++ b/tcl_tests/enc.try @@ -0,0 +1,222 @@ +#!/usr/bin/tclsh/ +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +set plain0 [getFile plain.enc] +file copy -force cfb0.enc $::test::dir +file copy -force cnt0.enc $::test::dir +file copy -force cbc0.enc $::test::dir +file copy -force aes0.enc $::test::dir +file copy -force plain.enc $::test::dir +file copy -force magma_plain.enc $::test::dir +file copy -force magma_acpkm_plain.enc $::test::dir +set cfb1 [getFile cfb1.enc] +set cbc1 [getFile cbc1.enc] +set cnt1 [getFile cnt1.enc] +set aes1 [getFile aes1.enc] +set magma1 [getFile magma1.enc] +set macpkm1 [getFile macpkm1.enc] +cd $::test::dir +file delete cfb1.enc cbc1.enc cnt1.enc aes1.enc magma1.enc magma1.enc macpkm1.enc +start_tests "Тесты на команду enc" + +save_env2 {CRYPT_PARAMS} +if [info exists env(CRYPT_PARAMS)] {unset env(CRYPT_PARAMS)} + +test -createsfiles cfb0.dec "Decrypting etalon file in CFB mode" { + openssl "enc -gost89 -d -md md5 -in cfb0.enc -out cfb0.dec -k 1234567890 -p" + getFile cfb0.dec +} 0 $plain0 + +test -createsfiles cnt0.dec "Decrypting etalon file CNT mode" { + openssl "enc -gost89-cnt -d -md md5 -in cnt0.enc -out cnt0.dec -k 1234567890 -p" + getFile cnt0.dec +} 0 $plain0 + +test -createsfiles cbc0.dec "Decrypting etalon file in CBC mode" { + openssl "enc -gost89-cbc -d -md md5 -in cbc0.enc -out cbc0.dec -k 1234567890 -p" + getFile cbc0.dec +} 0 $plain0 + +test -createsfiles aes0.dec "Decrypting etalon file encrypted with AES" { + openssl "enc -aes-128-cbc -d -md md5 -in aes0.enc -out aes0.dec -k 1234567890" + getFile aes0.dec +} 0 $plain0 + + +test -createsfiles cfb1.enc "Encrypting etalon file in CFB mode" { + openssl "enc -gost89 -in plain.enc -out cfb1.enc -K 0D6F24152DB4B18CC4B0EE62F55FAD0BEADC26E7992C5EDF039114EC3F44EB08 -iv 8EE68900818CD1F9 -p" + getFile cfb1.enc +} 0 $cfb1 + +test -createsfiles cnt1.enc "Encrypting etalon file in CNT mode" { + openssl "enc -gost89-cnt -in plain.enc -out cnt1.enc -K EF164FDF5B1128DE44AFCC00A0323DC1090EC99DE9C6B085B0D2550AB9F1AF47 -iv 9AF32B4E2FB1DF3D -p" + getFile cnt1.enc +} 0 $cnt1 + +test -createsfiles cbc1.enc "Encrypting etalon file in CBC mode" { + openssl "enc -gost89-cbc -in plain.enc -out cbc1.enc -K F6AF8D0EDF555D164E3DDFA20615D7DF602B99A5ED4BD4103C4CA622D4544636 -iv 8264BBB5A072CDB5 -p" + getFile cbc1.enc +} 0 $cbc1 + +test -createsfiles aes1.enc "Encrypting etalon file with AES" { + openssl "enc -aes-128-cbc -in plain.enc -out aes1.enc -K D45358C3C6E711392E9F2AFF46C444B1 -iv 78E88EFC8F44B9C27C45C5FCC61DCD94 -p" + getFile aes1.enc +} 0 $aes1 + + +set plain "Test data to encrypt " +makeFile enc.dat $plain binary +set plain2 [string repeat "Test data for encrypt of big string\n" 64] +makeFile enc2.dat $plain2 binary + +test -createsfiles {cfb.enc} "Encrypting file in CFB mode" { + openssl "enc -gost89 -out cfb.enc -in enc.dat -k 1234567890 -p" + file isfile cfb.enc +} 0 1 + +test -createsfiles {cnt.enc} "Encrypting file in CNT mode" { + openssl "enc -gost89-cnt -out cnt.enc -in enc.dat -k 1234567890 -p" + file isfile cnt.enc +} 0 1 + +test -createsfiles {cbc.enc} "Encrypting file in CBC mode" { + openssl "enc -gost89-cbc -out cbc.enc -in enc.dat -k 1234567890 -p" + file isfile cbc.enc +} 0 1 + +test -createsfiles aes.enc "Encrypting file using aes" { + openssl "enc -aes-128-cbc -out aes.enc -in enc.dat -k 1234567890" + file isfile aes.enc +} 0 1 + +test "Ciphered text in CFB mode differs from plain text" { + set ciphered [getFile cfb.enc binary] + string first $ciphered $plain +} 0 -1 + +test "Ciphered text in CNT mode differs from plain text" { + set ciphered [getFile cnt.enc binary] + string first $ciphered $plain +} 0 -1 + +test "Ciphered text in CBC mode differs from plain text" { + set ciphered [getFile cbc.enc binary] + string first $ciphered $plain +} 0 -1 + +test "Ciphered with aes text differs from plain text" { + set ciphered [getFile aes.enc binary] + string first $ciphered $plain +} 0 -1 + +test -createsfiles cfb.dec "Decrypting file, encrypted in CFB mode" { + openssl "enc -gost89 -d -in cfb.enc -out enc.dec -k 1234567890 -p" + getFile enc.dec +} 0 $plain + +test -createsfiles cnt.dec "Decrypting file, encrypted in CNT mode" { + openssl "enc -gost89-cnt -d -in cnt.enc -out cnt.dec -k 1234567890 -p" + getFile cnt.dec +} 0 $plain + +test -createsfiles cbc.dec "Decrypting file, encrypted in CBC mode" { + openssl "enc -gost89-cbc -d -in cbc.enc -out cbc.dec -k 1234567890 -p" + getFile cbc.dec +} 0 $plain + +test -createsfiles aes.dec "Decrypting file encrypted with aes" { + openssl "enc -aes-128-cbc -d -in aes.enc -out aes.dec -k 1234567890" + getFile aes.dec +} 0 $plain + + +test -createsfiles {cfb2.enc} "Encrypting more than 1KB" { + if [info exists env(CRYPT_PARAMS)] {unset env(CRYPT_PARAMS)} + openssl "enc -gost89 -out cfb2.enc -in enc2.dat -k 1234567890 -p" + file isfile cfb2.enc +} 0 1 + +test -createsfiles {cfb2.dec} "Decrypting more than 1Kb" { + openssl "enc -d -gost89 -out cfb2.dec -in cfb2.enc -k 1234567890 -p" + getFile cfb2.dec +} 0 $plain2 + +test -createsfiles {cnt2.enc} "Encrypting more than 1KB in CNT mode" { + if [info exists env(CRYPT_PARAMS)] {unset env(CRYPT_PARAMS)} + openssl "enc -gost89-cnt -out cnt2.enc -in enc2.dat -k 1234567890 -p" + file isfile cnt2.enc +} 0 1 + +test -createsfiles {cnt2.dec} "Decrypting more than 1Kb in CNT mode" { + openssl "enc -d -gost89-cnt -out cnt2.dec -in cnt2.enc -k 1234567890 -p" + getFile cnt2.dec +} 0 $plain2 + +test -createsfiles {cnc2.enc} "Encrypting more than 1KB in CBC mode" { + if [info exists env(CRYPT_PARAMS)] {unset env(CRYPT_PARAMS)} + openssl "enc -gost89-cbc -out cbc2.enc -in enc2.dat -k 1234567890 -p" + file isfile cbc2.enc +} 0 1 + +test -createsfiles {cbc2.dec} "Decrypting more than 1Kb in CBC mode" { + openssl "enc -d -gost89-cbc -out cbc2.dec -in cbc2.enc -k 1234567890 -p" + getFile cbc2.dec +} 0 $plain2 + +test -skip {![file exists enc2.dat]} -createsfiles {cfb3.enc} "Encrypting with paramset TC26 (symbolic)" { + set env(CRYPT_PARAMS) "id-tc26-gost-28147-param-Z" + openssl "enc -gost89 -out cfb3.enc -in enc2.dat -k 1234567890 -p" + file isfile cfb3.enc +} 0 1 + +test -skip {![file exists cfb3.enc]} -createsfiles {cfb3.dec1} "Decrypting with paramset TC26 (OID)" { + set env(CRYPT_PARAMS) "1.2.643.7.1.2.5.1.1" + openssl "enc -gost89 -d -in cfb3.enc -out cfb3.dec1 -k 1234567890 -p" + getFile cfb3.dec1 +} 0 $plain2 + +test -skip {![file exists enc2.dat]} -createsfiles {cbc3.enc} "Encrypting in CBC mode with paramset RIC 1 (symbolic)" { + set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-RIC-1-ParamSet" + openssl "enc -gost89-cbc -out cbc3.enc -in enc2.dat -k 1234567890 -p" + file isfile cbc3.enc +} 0 1 + +test -skip {![file exists cbc3.enc]} -createsfiles {cbc3.dec1} "Decrypting in CBC mode with paramset RIC 1 (OID)" { + set env(CRYPT_PARAMS) "1.2.643.2.2.31.7" + openssl "enc -gost89-cbc -d -in cbc3.enc -out cbc3.dec1 -k 1234567890 -p" + getFile cbc3.dec1 +} 0 $plain2 +restore_env2 {CRYPT_PARAMS} + +save_env2 {CRYPT_PARAMS OPENSSL_CONF} +test -skip {![file exists cfb3.enc]} -createsfiles {cfb3.dec2} "Decrypting with default params" { + if [info exists env(CRYPT_PARAMS)] {unset env(CRYPT_PARAMS)} + makeFile enc1.cnf [regsub -all "\n\\s*CRYPT_PARAMS\\s*=\[\^\n]*" [getConfig] ""] + set ::env(OPENSSL_CONF) [file join [pwd] enc1.cnf] + openssl "enc -gost89 -d -in cfb3.enc -out cfb3.dec2 -k 1234567890 -p" + getFile cfb3.dec2 +} 0 $plain2 +restore_env2 {CRYPT_PARAMS OPENSSL_CONF} + +save_env2 {CRYPT_PARAMS} +test -skip {![file exists cfb3.enc]} -createsfiles {cfb3.dec3} "Decrypting with wrong explicitely set" { + set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-B-ParamSet" + openssl "enc -gost89 -d -in cfb3.enc -out cfb3.dec3 -k 1234567890 -p" + string equal [getFile cfb3.dec3] $plain2 +} 0 0 + +restore_env2 {CRYPT_PARAMS} + +test -createsfiles magma1.enc "Encrypting etalon file (Magma-CTR)" { + openssl "enc -magma-ctr -K ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff -iv 1234567800000000 -in magma_plain.enc -out magma1.enc" + getFile magma1.enc +} 0 $magma1 + +test -createsfiles macpkm1.enc "Encrypting etalon file (Magma-ACPKM)" { + openssl "enc -id-tc26-cipher-gostr3412-2015-magma-ctracpkm -K F797256845F36CF075603445CD322BACC3834032BC425E4D3C8495236F7B6CAF -iv 00000FFF00000000 -in magma_acpkm_plain.enc -out macpkm1.enc" + getFile macpkm1.enc +} 0 $macpkm1 + + +end_tests diff --git a/tcl_tests/engine.try b/tcl_tests/engine.try new file mode 100644 index 0000000..2090e59 --- /dev/null +++ b/tcl_tests/engine.try @@ -0,0 +1,33 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Тесты на команду engine" + +switch -exact [engine_name] { + "ccore" {set list " \[RAND, gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, id-tc26-cipher-gostr3412-2015-magma-ctracpkm, magma-ctr, magma-ofb, magma-cbc, magma-cfb, grasshopper-ecb, grasshopper-cbc, grasshopper-ofb, grasshopper-cfb, grasshopper-ctr, id-tc26-cipher-gostr3412-2015-kuznyechik-ctracpkm, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, gost2001, id-GostR3410-2001DH, gost-mac, gost2012_256, gost2012_512, gost-mac-12\]\n"} + "open" {set list "(gost) Reference implementation of GOST engine\n \[gost89, gost89-cnt, gost89-cnt-12, gost89-cbc, md_gost94, gost-mac, md_gost12_256, md_gost12_512, gost-mac-12, gost2001, gost-mac, gost2012_256, gost2012_512, gost-mac-12\]\n"} +} + + +makeFile no_engine.cnf [regsub -all "\n\\s*engines\\s*=\\s*engines_section\[\^\n]*" [getConfig] ""] + +save_env2 {OPENSSL_CONF} +set env(OPENSSL_CONF) [file join [pwd] no_engine.cnf] + +test "Проверяем поддержку российских алгоритмов" { + grep "gost" [openssl "engine -c $env(ENGINE_NAME)"] +} 0 $list + +if {[engine_name] == "ccore"} { +test "Получение списка конфигурационных параметров" { + openssl "engine -v cryptocom" +} 0 "(cryptocom) Cryptocom GOST engine + RNG, RNG_PARAMS, CRYPT_PARAMS, CCENGINE_LICENSE, GOST_PBE_HMAC +" +} + +restore_env2 {OPENSSL_CONF} + +end_tests diff --git a/tcl_tests/enums.tcl b/tcl_tests/enums.tcl new file mode 100644 index 0000000..2709286 --- /dev/null +++ b/tcl_tests/enums.tcl @@ -0,0 +1,23 @@ +set f [open enums2tcl.c w] +puts $f "#include \"../ccore/ccapi.h\"" +puts $f "#include \"../ccore/ccrdscb.h\"" +puts $f "#include " +puts $f "int main (void) {" +set inc [open ../ccore/ccapi.h r] +while {[gets $inc line] >= 0} { + if [regexp {\bcc_rc_\w+} $line code] { + puts $f "printf(\"set $code %d\\n\", $code);" + } +} +close $inc +set inc [open ../ccore/ccrdscb.h r] +while {[gets $inc line] >= 0} { + if [regexp {\bcc_rds_cb_(rc|op|stage)_\w+} $line code] { + puts $f "printf(\"set $code %d\\n\", $code);" + } +} +close $inc +puts $f "return 0;" +puts $f "}" +close $f + diff --git a/tcl_tests/getengine.tcl b/tcl_tests/getengine.tcl new file mode 100644 index 0000000..49176c0 --- /dev/null +++ b/tcl_tests/getengine.tcl @@ -0,0 +1,37 @@ +#!/usr/bin/tclsh +lappend auto_path . +package require ossltest + +proc getConfigLine {var {section ""}} { + global config + if {[string length $section]} { + if {[regexp -indices "\n\\s*\\\[\\s*$section\\s*\\\]\\s*\n" $config start]} { + set start [lindex $start 1] + } else { + return -code error "Section $section is not found" + } + } else { + set start 0 + } + if {[regexp -indices "\n\\s*\\\[\[^\n\]+\\\]\\s*\n" [string range $config $start end] end]} { + set end [expr $start+[lindex $end 0]] + } else { + set end end + } + if {![regexp "\n\\s*$var\\s*=\\s*(\\S\[^\n\]+?)\\s*\n" "\n[string range $config $start $end]" => value]} { + return -code error "No variable $var in section $section" + } + return $value +} + +set config [getConfig] + +set openssl_def [getConfigLine openssl_conf] + +set engine_section [getConfigLine {[^#]+} [getConfigLine engines $openssl_def ]] + +puts [getConfigLine engine_id $engine_section] + + + + diff --git a/tcl_tests/http.tcl b/tcl_tests/http.tcl new file mode 100644 index 0000000..6121617 --- /dev/null +++ b/tcl_tests/http.tcl @@ -0,0 +1,28 @@ +# -*- coding: cp1251 -*- +# +# Получает в командной строке URL и (опционально) строку для поиска +# сертификата. Выполняет HTTP-запрос и возрвщает результат +# В строке для поиска сертификата можно использовать прямые слэши вместо +# обратных. + +if {!$argc || $argc>2} { + puts stderr "Usage $argv0 url \[cert-spec\]" +} + +set url [lindex $argv 0] +if {$argc==2} { + set certspec [string map {/ \\} [lindex $argv 1]] +} + + +puts Started + +package require tcom +set hh [::tcom::ref createobject WinHttp.WinHttpRequest.5.1] +$hh Open GET $url 0 +if {[info exists certspec]} { + puts "Setting Client Certificate $certspec" + $hh SetClientCertificate $certspec +} +$hh Send +puts [$hh ResponseText] diff --git a/tcl_tests/hwkeys.tcl b/tcl_tests/hwkeys.tcl new file mode 100644 index 0000000..8c86a82 --- /dev/null +++ b/tcl_tests/hwkeys.tcl @@ -0,0 +1,229 @@ +# -*- coding: cp1251 -*- +package require testlib + +start_tests "Работа с аппаратными носителями" + +if [info exists ::env(BASE_OPENSSL_CONF)] { + set openssl_cnf [myfile openssl.cnf] + set bf [open $::env(BASE_OPENSSL_CONF) r] + set f [open $openssl_cnf w] + set engines {} + set in_engines 0 + while {[gets $bf line] >= 0} { + puts $f $line + if {[regexp {^\[engine_section\]} $line]} { + puts $f "ce_filecnt_keys = cefk_section" + } + } + close $bf + if {$tcl_platform(platform) eq "windows"} { + set lib_prefix "" + set lib_suffix ".dll" + } else { + set lib_prefix "lib" + set lib_suffix ".so" + } + puts $f "\[cefk_section\] +dynamic_path = \$ENV::TEST_ENGINE_DIR/${lib_prefix}ce_filecnt_keys$lib_suffix +engine_id = ce_filecnt_keys +default_algorithms = ALL +\[req\] +prompt=no +distinguished_name = req_dn +\[ req_dn \] +OU=OpenSSL Team +L=Moscow +CN=Dummy user +emailAddress=openssl@cryptocom.ru +O=Cryptocom +C=RU" + close $f + file copy [file dirname $env(BASE_OPENSSL_CONF)]/cryptocom.lic [file dirname $openssl_cnf]/cryptocom.lic + set ::env(OPENSSL_CONF) $openssl_cnf + puts [logchannel] "OPENSSL_CONF=$::env(OPENSSL_CONF)" + set ::env(TEST_ENGINE_DIR) [regsub {(/[^/]+)$} $::env(ENGINE_DIR) {/t\1}] + puts [logchannel] "TEST_ENGINE_DIR=$::env(TEST_ENGINE_DIR)" +} + +set cnt_pln_file [myfile cnt_pln] +set cnt_pln_dot_file [myfile cnt.pln.S] +set cnt_pln FILECNT=$cnt_pln_file +set cnt_enc_file [myfile cnt_enc] +set cnt_enc FILECNT=$cnt_enc_file +file copy -force ../cnt.pln $cnt_pln_file +file copy -force ../cnt.pln $cnt_pln_dot_file +file copy -force ../cnt.pln default_file_container +file copy -force ../cnt.enc $cnt_enc_file +set cntname "test keys" + +file delete $cnt_enc_file.cmd $cnt_pln_file.cmd +eval [exec enums2tcl] + +foreach K {S X} { + set cert$K [myfile cert$K.pem] + set pubk$K [myfile pubk$K.pem] + upvar 0 cert$K cert pubk$K pubk + + test -title "$K: сертификат и его открытый ключ" -id cert$K { + run openssl req -new -x509 -key $cnt_pln.$K -keyform ENGINE -engine cryptocom -out $cert + run openssl x509 -pubkey -noout -in $cert + file rename _stdout $pubk + } + + test -title "$K: Подписываем файл закрытым ключом" -id sign$K -dep cert$K { + run openssl dgst -md_gost94 -sign $cnt_pln.$K -keyform ENGINE -engine cryptocom -out $cert.sig $cert + } + + test -title "$K: Проверяем подпись на закрытом ключе" -dep sign$K { + run openssl dgst -md_gost94 -prverify $cnt_pln.$K -keyform ENGINE -engine cryptocom -signature $cert.sig $cert + } + + test -title "$K: Проверяем подпись на открытом ключе" -dep sign$K { + run openssl dgst -md_gost94 -verify $pubk -signature $cert.sig $cert + } + + test -title "$K: Подписываем файл закрытым ключом, контейнер с именем" -id sign$K -dep cert$K { + run openssl dgst -md_gost94 -sign $cnt_pln:$cntname.$K -keyform ENGINE -engine cryptocom -out $cert.sig $cert + } + + test -title "$K: Подписываем файл запароленным закрытым ключом" -dep cert$K { + run openssl dgst -md_gost94 -sign $cnt_enc.$K -keyform ENGINE -engine cryptocom -out $cert.sig -passin pass:abcdefghijklmnopqrstuvwxyz1234567890 $cert + run openssl dgst -md_gost94 -verify $pubk -signature $cert.sig $cert + } + +} + +test -title "Читаем по полной спецификации" { + run hwkeys -load $cnt_pln:$cntname.S +} + +test -title "Читаем без имени контейнера" { + run hwkeys -load $cnt_pln.S +} + +test -title "Читаем без имени носителя" { + run hwkeys -load FILECNT:$cntname.S +} + +test -title "Читаем без имен контейнера и носителя" { + run hwkeys -load FILECNT.S +} + +test -title "Читаем с именем носителя, содержащим .S" { + run hwkeys -load FILECNT=$cnt_pln_dot_file.S +} + +end_tests + +proc write_cmd_file {filename args} { + set f [open filename w] + fconfigure $f -encoding binary + puts -nonewline $f [binary format c* $args] + close $f +} + +test -title "Читаем, нет носителя, нет коллбэка" { + write_cmd_file $cnt_pln_file.cmd $cc_rc_no_contact + run -fail -stderr {regex {cc_rds_read_key.*==cc_rc_no_contact.*load_key failed}} \ + hwkeys -no-cb -load $cnt_pln.S +} + +test -title "Читаем, нет носителя, есть коллбэк, носитель дали" + +test -title "Читаем, нет носителя, есть коллбэк, запрос отменили" + +test -title "Читаем, есть носитель, нет контейнера" + +test -title "Читаем, не тот контейнер, нет коллбэка" + +test -title "Читаем, не тот контейнер, есть коллбэк, носитель поменяли, опять не тот, еще раз поменяли, теперь тот" + +test -title "Читаем, не тот контейнер, есть коллбэк, запрос отменили" + +test -title "Читаем, нет этого ключа (другой есть)" + +test -title "Читаем, ошибка чтения, нет коллбэка" + +test -title "Читаем, ошибка чтения, устранена" + +test -title "Читаем, ошибка чтения, таймаут" + +test -title "Читаем, ошибка чтения, отмена" + +test -title "Читаем, не сошлась CRC ключа" + +test -title "Читаем парольный, даем пароль" + +test -title "Читаем парольный, даем пароль со второй попытки" + +test -title "Читаем парольный, ошибка возврата пароля" + +test -title "Читаем парольный, отмена" + +test -title "Пишем в свежий контейнер" + +test -title "Проверяем подписью, что это - тот же самый ключ" + +test -title "Пишем в тот же контейнер второй ключ зашифрованным" + +test -title "Проверяем подписью, что оно" + +test -title "Пишем безымянный" + +test -title "Пишем зашифрованный, пароли совпадают со второй попытки" + +test -title "Пишем зашифрованный, ошибка получения пароля" + +test -title "Пишем зашифрованный, отмена" + +test -title "Ошибка записи, нет коллбэка" + +test -title "Ошибка записи, вернули носитель" + +test -title "Ошибка записи, таймаут" + +test -title "Ошибка записи, отмена" + +test -title "Нет носителя, нет коллбэка" + +test -title "Нет носителя, дали" + +test -title "Нет носителя, таймаут" + +test -title "Нет носителя, отмена" + +test -title "Не тот контейнер, нет перезаписи, нет коллбэка" + +test -title "Не тот контейнер, нет перезаписи, сменили носитель" + +test -title "Не тот контейнер, нет перезаписи, таймаут" + +test -title "Не тот контейнер, нет перезаписи, отмена" + +test -title "Не тот контейнер, есть перезапись" + +test -title "Ключ есть, перезапись запрещена" + +test -title "Ключ есть, перезапись разрешена" + +test -title "Затираем" + +test -title "Затираем, нет носителя, нет коллбэка" + +test -title "Затираем, нет носителя, дали носитель" + +test -title "Затираем, нет носителя, таймаут" + +test -title "Затираем, нет носителя, отмена" + +test -title "Затираем, не тот контейнер, нет коллбэка" + +test -title "Затираем, не тот контейнер, сменили носитель" + +test -title "Затираем, не тот контейнер, таймаут" + +test -title "Затираем, не тот контейнер, отмена" + +test -title "Затираем контейнер без имени, даем без имени" + +test -title "Затираем контейнер без имени, даем с именем" diff --git a/tcl_tests/interop.try b/tcl_tests/interop.try new file mode 100644 index 0000000..ffabc97 --- /dev/null +++ b/tcl_tests/interop.try @@ -0,0 +1,156 @@ +#!/usr/bin/tclsh + +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} +if {[info exists env(PKG_PATH)]} { + lappend auto_path $env(PKG_PATH) +} else { + lappend auto_path [file dirname [info script]] +} +if {![info exists env(OTHER_DIR)]} { + puts stderr "Environment variable OTHER_DIR not set" + exit 1 +} else { + set data_dir $env(OTHER_DIR) +} +if {[file normalize $data_dir] == "[pwd]"} { + set suffix _bck +} elseif {[file normalize $data_dir] == [file normalize [pwd]/../OtherVersion]} { + set suffix _oth +} else { + set suffix _fwd +} +package require ossltest +#cd z +set ::test::suffix $suffix +cd $::test::dir +start_tests "Интероперабельность, сравнение с $data_dir" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B} +} +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:} +} + +test -createsfiles cfb2.$suffix\ +"Расшифрование текста, зашифрованного на пароле в режиме CFB" { + set plain [getFile $data_dir/enc2.dat] + openssl "enc -gost89 -d -in $data_dir/cfb2.enc -out cfb2.$suffix -k 1234567890 -p" + set result [getFile cfb2.$suffix] + expr {[string equal $plain $result]?1:$result} +} 0 1 + +test -createsfiles cnt2.$suffix\ +"Расшифрование текста, зашифрованного на пароле в режиме CNT" { + set plain [getFile $data_dir/enc2.dat] + openssl "enc -gost89-cnt -d -in $data_dir/cnt2.enc -out cnt2.$suffix -k 1234567890 -p" + set result [getFile cnt2.$suffix] + expr {[string equal $plain $result]?1:$result} +} 0 1 + +test -createsfiles cbc2.$suffix\ +"Расшифрование текста, зашифрованного на пароле в режиме CBC" { + set plain [getFile $data_dir/enc2.dat] + openssl "enc -gost89-cbc -d -in $data_dir/cbc2.enc -out cbc2.$suffix -k 1234567890 -p" + set result [getFile cbc2.$suffix] + expr {[string equal $plain $result]?1:$result} +} 0 1 + +save_env2 {CRYPT_PARAMS} +test -createsfiles cbc3.$suffix\ +"Расшифрование текста, зашифрованного в режиме CBC с параметрами РИК 1" { + set plain [getFile $data_dir/enc2.dat] + set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-RIC-1-ParamSet" + openssl "enc -gost89-cbc -d -in $data_dir/cbc3.enc -out cbc3.$suffix -k 1234567890 -p" + set result [getFile cbc3.$suffix] + expr {[string equal $plain $result]?1:$result} +} 0 1 +restore_env2 {CRYPT_PARAMS} + + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username $data_dir/U_smime_$alg_fn + set userdir $data_dir/U_smime_${alg_fn} + switch -glob $alg { + gost2012* {set CA_dir_suffix CA-2012} + * {set CA_dir_suffix CA} + } + + +test "Проверка заявки $alg" { + grep "verif" [openssl "req -verify -in $username/req.pem"] +} 0 {verify OK +} + +test "Проверка сертификата $alg" { + grep "cert.pem" [openssl "verify -CAfile $data_dir/smime$CA_dir_suffix/cacert.pem $userdir/cert.pem"] +} 0 "$userdir/cert.pem: OK +" + +test "Проверка CRL" { + grep verify [openssl "crl -in $data_dir/test.crl -noout -CAfile $data_dir/test_crl_cacert.pem"] +} 0 "verify OK +" + +test "Проверка документа, подписанного $alg, smime" { + grep Veri [openssl "smime -verify -text -in $data_dir/sign_$alg_fn.msg -out verified.$suffix -CAfile $data_dir/smime$CA_dir_suffix/cacert.pem -certfile $username/cert.pem"] +} 0 "Verification successful +" + +set username $data_dir/U_cms_$alg_fn +test "Проверка документа, подписанного $alg, cms" { + grep Veri [openssl "cms -verify -text -in $data_dir/cms_sign_$alg_fn.msg -out cms_verified.$suffix -CAfile $data_dir/cms$CA_dir_suffix/cacert.pem -certfile $username/cert.pem"] +} 0 "Verification successful +" + +test -createsfiles [list extracted_cert.pem.$suffix extracted_key.pem.$suffix] "Разбираем pkcs12 c алгоритмом $alg" { + openssl "pkcs12 -in $data_dir/U_pkcs12_$alg_fn/pkcs12.p12 -nodes -out dump.pem.$suffix -password pass:12345" + set dump [getFile dump.pem.$suffix] + set lextr [regexp -all -inline "\n-----BEGIN .*?\n-----END \[^\n\]+-----\n" $dump] + + list [llength $lextr] [expr {[lindex $lextr 0] eq "\n[getFile $data_dir/U_pkcs12_$alg_fn/cert.pem]"}] [expr {[lindex $lextr 1] eq "\n[openssl "pkcs8 -nocrypt -topk8 -in $data_dir/U_pkcs12_$alg_fn/seckey.pem"]"}] + +} 0 {2 1 1} + + +} + +save_env2 {CRYPT_PARAMS} +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param + } else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + } + set alg_fn [make_fn $enc_tuple] + set username U_enc_$alg_fn + +test "Расшифрование документа на keyexchange $alg, smime" { + set expected [getFile $data_dir/encrypt.dat] + openssl "smime -decrypt -in $data_dir/enc_${alg_fn}.msg -recip $data_dir/U_enc_${alg_fn}/cert.pem -inkey $data_dir/U_enc_${alg_fn}/seckey.pem -out decrypt1.$alg_fn.$suffix" + set result [getFile decrypt1.$alg_fn.$suffix] + string eq $expected $result +} 0 1 + +test "Расшифрование документа на keyexchange $alg, cms" { + set expected [getFile $data_dir/encrypt.dat] + openssl "cms -decrypt -in $data_dir/cms_enc_${alg_fn}.msg -recip $data_dir/U_cms_enc_${alg_fn}/cert.pem -inkey $data_dir/U_cms_enc_${alg_fn}/seckey.pem -out cms_decrypt1.$alg_fn.$suffix" + set result [getFile cms_decrypt1.$alg_fn.$suffix] + string eq $expected $result +} 0 1 + + +} +restore_env2 {CRYPT_PARAMS} +end_tests diff --git a/tcl_tests/kbstrike.exe b/tcl_tests/kbstrike.exe new file mode 100755 index 0000000000000000000000000000000000000000..542919afe689cf8e6c29e757562c95adefaa9a69 GIT binary patch literal 64512 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 C8UeNd literal 0 HcmV?d00001 diff --git a/tcl_tests/magma1.enc b/tcl_tests/magma1.enc new file mode 100644 index 0000000..c14cb49 --- /dev/null +++ b/tcl_tests/magma1.enc @@ -0,0 +1 @@ +N˜ —·¹<>% “Öè]im†ˆ²ÛïVŽ¶€«R¡- \ No newline at end of file diff --git a/tcl_tests/magma_acpkm_plain.enc b/tcl_tests/magma_acpkm_plain.enc new file mode 100644 index 0000000000000000000000000000000000000000..d67f00c217946c15808fe41ab63cf9e5210dc3ee GIT binary patch literal 1032 bcmZQz7zLvtFd70QH3Z(iTiWwXV|xt%9)<-@ literal 0 HcmV?d00001 diff --git a/tcl_tests/magma_enc b/tcl_tests/magma_enc new file mode 100644 index 0000000..c14cb49 --- /dev/null +++ b/tcl_tests/magma_enc @@ -0,0 +1 @@ +N˜ —·¹<>% “Öè]im†ˆ²ÛïVŽ¶€«R¡- \ No newline at end of file diff --git a/tcl_tests/magma_plain b/tcl_tests/magma_plain new file mode 100644 index 0000000..12f236b --- /dev/null +++ b/tcl_tests/magma_plain @@ -0,0 +1,2 @@ +’Þðk< +YÛTÇø J˜û.g¨L‰@›µ~A \ No newline at end of file diff --git a/tcl_tests/magma_plain.enc b/tcl_tests/magma_plain.enc new file mode 100644 index 0000000..12f236b --- /dev/null +++ b/tcl_tests/magma_plain.enc @@ -0,0 +1,2 @@ +’Þðk< +YÛTÇø J˜û.g¨L‰@›µ~A \ No newline at end of file diff --git a/tcl_tests/make_other.sh b/tcl_tests/make_other.sh new file mode 100755 index 0000000..ed9d9e8 --- /dev/null +++ b/tcl_tests/make_other.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# -*- coding: cp1251 -*- + +# Создает подкаталог OtherVersion, копирует в него данные для тестирования +# совместимости с другими версиями. +# Данные берутся из результатов прогона тестов для открытого энжина +# (поскольку именно он гарантированно умеет все нужные алгоритмы, +# включая устаревшие). + +TESTDIR=`hostname`-gost +SAVEDIR=OtherVersion +if ! [ -d ${TESTDIR} ]; then + echo $TESTDIR does not exist. + exit 1 +fi +[ -d ${SAVEDIR} ] && rm -fr ${SAVEDIR} +mkdir ${SAVEDIR} +cd ${TESTDIR} +cp -rp enc.enc enc.dat ../$SAVEDIR +cp -rp smimeCA test.crl test_crl_cacert.pem ../$SAVEDIR +cp -rp U_smime_* sign_*.msg ../$SAVEDIR +cp -rp cmsCA U_cms_* cms_sign_*.msg ../$SAVEDIR +cp -rp U_pkcs12_* ../$SAVEDIR +cp -rp encrypt.dat U_enc_* enc_*.msg ../$SAVEDIR +cp -rp U_cms_enc_* cms_enc_*.msg ../$SAVEDIR diff --git a/tcl_tests/mkn2o.tcl b/tcl_tests/mkn2o.tcl new file mode 100644 index 0000000..256b2a0 --- /dev/null +++ b/tcl_tests/mkn2o.tcl @@ -0,0 +1,28 @@ +proc oid {oid name types} { + global new_name2oid + set new_name2oid($name) $oid +} + +source [lindex $argv 0] +source name2oid.tcl + +set differ 0 +foreach name [array names name2oid] { + if {![info exists new_name2oid($name)] || $new_name2oid($name) != $name2oid($name)} {set differ 1} +} +if {!$differ} { + foreach name [array names new_name2oid] { + if {![info exists name2oid($name)]} {set differ 1} + } +} + +if {$differ} { + set n2of [open name2oid.tcl w] + puts $n2of "array set name2oid {" + foreach name [lsort [array names new_name2oid]] { + puts $n2of "$name $new_name2oid($name)" + } + puts $n2of "}" + close $n2of +} + diff --git a/tcl_tests/mkoidf.tcl b/tcl_tests/mkoidf.tcl new file mode 100644 index 0000000..c08e015 --- /dev/null +++ b/tcl_tests/mkoidf.tcl @@ -0,0 +1,5 @@ +proc oid {oid name types} { + puts "$oid $name GOST $name $oid" +} + +source [lindex $argv 0] diff --git a/tcl_tests/name2oid.tcl b/tcl_tests/name2oid.tcl new file mode 100644 index 0000000..fc2e639 --- /dev/null +++ b/tcl_tests/name2oid.tcl @@ -0,0 +1,44 @@ +array set name2oid { +crypt89_cc 1.2.643.2.2.21 +mac89 1.2.643.2.2.22 +pk_sign94_cc 1.2.643.2.9.1.5.3 +pk_sign94_cp 1.2.643.2.2.20 +pk_sign01_cc 1.2.643.2.9.1.5.4 +pk_sign01_cp 1.2.643.2.2.19 +pk_sign12_256 1.2.643.7.1.1.1.1 +pk_sign12_512 1.2.643.7.1.1.1.2 +hash_94 1.2.643.2.2.9 +hash_12_256 1.2.643.7.1.1.2.2 +hash_12_512 1.2.643.7.1.1.2.3 +hash_with_sign94_cc 1.2.643.2.9.1.3.3 +hash_with_sign94_cp 1.2.643.2.2.4 +hash_with_sign01_cc 1.2.643.2.9.1.3.4 +hash_with_sign01_cp 1.2.643.2.2.3 +hash_with_sign12_256 1.2.643.7.1.1.3.2 +hash_with_sign12_512 1.2.643.7.1.1.3.3 +param_encr_cc 1.2.643.2.9.1.6.1 +param_encr_cpa 1.2.643.2.2.31.1 +param_encr_cpb 1.2.643.2.2.31.2 +param_encr_cpc 1.2.643.2.2.31.3 +param_encr_cpd 1.2.643.2.2.31.4 +param_encr_cptest 1.2.643.2.2.31.0 +param_encr_tc 1.2.643.7.1.2.5.1.1 +param_hash_94 1.2.643.2.2.30.1 +param_pubkey94_cpa 1.2.643.2.2.32.2 +param_pubkey94_cpb 1.2.643.2.2.32.3 +param_pubkey94_cpc 1.2.643.2.2.32.4 +param_pubkey94_cpd 1.2.643.2.2.32.5 +param_pubkey94_cpxcha 1.2.643.2.2.33.1 +param_pubkey94_cpxchb 1.2.643.2.2.33.2 +param_pubkey94_cpxchc 1.2.643.2.2.33.3 +param_pubkey01_cc 1.2.643.2.9.1.8.1 +param_pubkey01_cpa 1.2.643.2.2.35.1 +param_pubkey01_cpb 1.2.643.2.2.35.2 +param_pubkey01_cpc 1.2.643.2.2.35.3 +param_pubkey01_cptest 1.2.643.2.2.35.0 +param_pubkey01_cpxcha 1.2.643.2.2.36.0 +param_pubkey01_cpxchb 1.2.643.2.2.36.1 +param_pubkey12_512_0 1.2.643.7.1.2.1.2.0 +param_pubkey12_512_A 1.2.643.7.1.2.1.2.1 +param_pubkey12_512_B 1.2.643.7.1.2.1.2.2 +} diff --git a/tcl_tests/name2oid.tst b/tcl_tests/name2oid.tst new file mode 100644 index 0000000..e69de29 diff --git a/tcl_tests/nopath.try b/tcl_tests/nopath.try new file mode 100644 index 0000000..c3664ae --- /dev/null +++ b/tcl_tests/nopath.try @@ -0,0 +1,68 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Подгрузка engine без явно указанного dynamic_path" + +set config [getConfig] +regexp {\ndynamic_path\s*=\s*(\S[^\n]+)} $config => path +if [file exist [file join [file dirname $::OPENSSL_CONF] cryptocom.lic]] { +file copy -force [file join [file dirname $::OPENSSL_CONF] cryptocom.lic] cryptocom.lic +} +if {[info exists path]} { + set env(OPENSSL_ENGINES) [subst -nocommands -nobackslashes [regsub {\$ENV::(\w+)} [file dirname $path] {$env(\1)}]] + puts $env(OPENSSL_ENGINES) + makeFile nodp.conf [regsub {\ndynamic_path\s*=\s*([^\n]+)} $config {}] + set env(OPENSSL_CONF) "[pwd]/nodp.conf" +} +test -platformex {[info exists path]} -createsfiles dgst.dat "Вычисление дайджеста md_gost94" { + makeFile dgst.dat [string repeat "Test data to digest.\n" 100] binary + grep "md_gost94\\(" [openssl "dgst -md_gost94 dgst.dat"] +} 0 "md_gost94\(dgst.dat)= 42e462ce1c2b4bf72a4815b7b4877c601f05e5781a71eaa36f63f836c021865c\n" + +set plain "Test data to encrypt" +test -platformex {[info exists path]} -createsfiles {enc.enc enc.dat} "Encrypting file in CFB mode" { + makeFile enc.dat $plain binary + openssl "enc -gost89 -out enc.enc -in enc.dat -k 1234567890 -p" + file isfile enc.enc +} 0 1 + +test -platformex {[info exists path]} -createsfiles {cnt.enc} "Encrypting file in CNT mode" { + makeFile enc.dat $plain binary + openssl "enc -gost89-cnt -out cnt.enc -in enc.dat -k 1234567890 -p" + file isfile cnt.enc +} 0 1 + +test -platformex {[info exists path]} -skip {![file exists enc.enc]} "Ciphered text in CFB mode differs from clear text" { + set ciphered [getFile enc.enc binary] + string first $ciphered $plain +} 0 -1 + +test -platformex {[info exists path]} -skip {![file exists cnt.enc]} "Ciphered text in CNT mode differs from clear text" { + set ciphered [getFile cnt.enc binary] + string first $ciphered $plain +} 0 -1 + +test -platformex {[info exists path]} -skip {![file exists enc.enc]} -createsfiles enc.dec "Decrypting file, encrypted in CFB mode" { + openssl "enc -gost89 -d -in enc.enc -out enc.dec -k 1234567890 -p" + getFile enc.dec +} 0 $plain + +test -platformex {[info exists path]} -skip {![file exists cnt.enc]} -createsfiles cnt.dec "Decrypting file, encrypted in CNT mode" { + openssl "enc -gost89-cnt -d -in cnt.enc -out cnt.dec -k 1234567890 -p" + getFile cnt.dec +} 0 $plain + +test -platformex {[info exists path]} "Вычисление MAC gost89" { + grep gost-mac [openssl "dgst -mac gost-mac -macopt key:12345678901234567890123456789012 dgst.dat"] +} 0 "GOST-MAC-gost-mac(dgst.dat)= 37f646d2\n" + +test -platformex {[info exists path]} -createsfiles nodp2001.key "Создание секретного ключа gost2001" { + makeSecretKey nodp2001 gost2001:A + file exists nodp2001/seckey.pem +} 0 1 + + +file delete cryptocom.lic +end_tests diff --git a/tcl_tests/ocsp.try b/tcl_tests/ocsp.try new file mode 100644 index 0000000..30c1218 --- /dev/null +++ b/tcl_tests/ocsp.try @@ -0,0 +1,153 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на OCSP-запросы и ответы" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2012_256:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2012_256:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_smime_$alg_fn + +test "Creating CA" { + makeCA ${testname}CA-$alg_fn $alg +} 0 1 + +after 1000 + +set server_args "-index $::test::ca/index.txt -rsigner $::test::ca/cacert.pem -rkey $::test::ca/private/cakey.pem -CA $::test::ca/cacert.pem -noverify" +set client_args "-issuer $::test::ca/cacert.pem -CAfile $::test::ca/cacert.pem" + +test "Создаем юзера" { + makeRegisteredUser U_ocsp_$alg_fn $alg + makeRegisteredUser U_ocsp2_$alg_fn $alg + file exists U_ocsp_$alg_fn/cert.pem +} 0 1 + +after 1000 + +test -createsfiles {request1.der} "Создаеем неподписанный запрос SHA1 хэш по сертификату" { + openssl "ocsp $client_args -cert U_ocsp_$alg_fn/cert.pem -reqout request1.der" + file exists request1.der +} 0 1 + +test -skip {![file exists request1.der]} "Анализируем OID-ы в запросе" { + extract_oids request1.der DER +} 0 " OBJECT :sha1 +" + +test -skip {![file exists request1.der]} -createsfiles {response1.der} "Формируем ГОСТ-подписанный ответ" { + openssl "ocsp $server_args -reqin request1.der -respout response1.der" +} 0 "" + +test -skip {![file exists request1.der]} "Анализируем OID-ы в ответе" { + extract_oids response1.der DER 30 +} 0 " OBJECT :sha1\n[mkObjList [hash_with_sign_long_name $alg] [hash_with_sign_long_name $alg] [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [hash_with_sign_long_name $alg]]" + + +test -skip {![file exists response1.der]} "Проверяем ГОСТ-подписанный ответ" { + openssl "ocsp $client_args -respin response1.der" + +} 0 "STDERR CONTENTS:\nResponse verify OK" + +test -skip {![file exists response1.der]} "Проверяем статус сертификата" { + grep "Cert Status" [openssl "ocsp -respin response1.der -text -CAfile $::test::ca/cacert.pem"] +} 0 " Cert Status: good\n" + +test -createsfiles request2.der "Формируем ГОСТ-подписанный запрос с хэшом SHA1 по сертификату" { + openssl "ocsp $client_args -cert U_ocsp_$alg_fn/cert.pem -signer U_ocsp_$alg_fn/cert.pem -signkey U_ocsp_$alg_fn/seckey.pem -reqout request2.der" + +} 0 "" + +test -skip {![file exists request2.der]} "Анализируем OID-ы в запросе" { + extract_oids request2.der DER +} 0 " OBJECT :sha1\n[mkObjList [hash_with_sign_long_name $alg] [hash_with_sign_long_name $alg] [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [hash_with_sign_long_name $alg]]" + + +test -createsfiles response2.der -skip {![file exists request2.der]} "Формируем ответ на подписанный запрос" { + openssl "ocsp $server_args -reqin request2.der -respout response2.der" + file exists response2.der +} 0 1 + +test -skip {![file exists response2.der]} "Проверяем ответ на запрос 2" { + grep "Response .erif" [openssl "ocsp $client_args -respin response2.der"] +} 0 "Response verify OK\n" + +test -createsfiles request3.der "Формируем запрос с ГОСТ-овским хэшом по сертификату" { + openssl "ocsp $client_args -[hash_short_name [alg_hash $alg]] -cert U_ocsp_$alg_fn/cert.pem -reqout request3.der" + file exists request3.der +} 0 1 + +test -skip {![file exists request3.der]} "Анализируем OID-ы в запросе" { + extract_oids request3.der DER +} 0 [mkObjList [hash_long_name $alg]] + +test -skip {![file exists request3.der]} -createsfiles response3.der "Формируем ответ на запрос с ГОСТ-овским хэшом" { + openssl "ocsp $server_args -reqin request3.der -respout response3.der" + file exists response3.der +} 0 1 + +test -skip {![file exists response3.der] } "Проверяем ответ на запрос 3" { + grep "Response .erif" [openssl "ocsp -[hash_short_name [alg_hash $alg]] $client_args -respin response3.der"] +} 0 "Response verify OK\n" + + +test -skip {![file exists response3.der]} "Проверяем статус сертификата" { + grep "Cert Status" [openssl "ocsp -respin response3.der -text -CAfile $::test::ca/cacert.pem"] +} 0 " Cert Status: good\n" + +test -createsfiles request4.der "Формируем запрос с ГОСТ-овским хэшом по serial" { + openssl "ocsp $client_args -[hash_short_name [alg_hash $alg]] -serial 0x11E -reqout request4.der" +} 0 "" + +test -skip {![file exists request4.der]} "Проверяем OID-ы в запросе 4" { + extract_oids request4.der DER +} 0 [mkObjList [hash_long_name $alg]] + + +test -skip {![file exists request4.der]} -createsfiles response4.der "Формируем ответ на запрос с ГОСТ-овским хэшом" { + openssl "ocsp $server_args -reqin request4.der -respout response4.der" + file exists response4.der +} 0 1 + +test -skip {![file exists response4.der] } "Проверяем ответ на запрос 4" { + grep "Response .erif" [openssl "ocsp $client_args -respin response4.der"] +} 0 "Response verify OK\n" + +test -createsfiles request5.der "Формируем запрос с двумя сертификатами и разными хэшами" { + openssl "ocsp $client_args -[hash_short_name [alg_hash $alg]] -cert U_ocsp_$alg_fn/cert.pem -sha1 -cert U_ocsp2_$alg_fn/cert.pem -reqout request5.der" +} 0 "" + +test -skip {![file exists request5.der]} "Проверяем OID-ы в запросе 5" { + extract_oids request5.der DER +} 0 "[mkObjList [hash_long_name $alg]] OBJECT :sha1\n" + + +test -skip {![file exists request5.der]} -createsfiles response5.der "Формируем ответ на запрос с двумя хэшами" { + openssl "ocsp $server_args -reqin request5.der -respout response5.der" + file exists response5.der +} 0 1 + +test -skip {![file exists response5.der] } "Проверяем ответ на запрос 5" { + grep "Response .erif" [openssl "ocsp -[hash_short_name [alg_hash $alg]] $client_args -respin response5.der"] +} 0 "Response verify OK\n" + +test -skip {![file exists response5.der]} "Проверяем статус сертификатoв" { + grep "Cert Status" [openssl "ocsp -respin response5.der -text -CAfile $::test::ca/cacert.pem"] +} 0 " Cert Status: good\n Cert Status: good\n" + +} + +end_tests + diff --git a/tcl_tests/oidfile b/tcl_tests/oidfile new file mode 100644 index 0000000..723e542 --- /dev/null +++ b/tcl_tests/oidfile @@ -0,0 +1,43 @@ +1.2.643.2.2.9 hash_94 GOST hash_94 1.2.643.2.2.9 +1.2.643.7.1.1.2.2 hash_12_256 GOST hash_12_256 1.2.643.7.1.1.2.2 +1.2.643.7.1.1.2.3 hash_12_512 GOST hash_12_512 1.2.643.7.1.1.2.3 +1.2.643.2.2.30.1 param_hash_94 GOST param_hash_94 1.2.643.2.2.30.1 +1.2.643.2.2.21 crypt89_cc GOST crypt89_cc 1.2.643.2.2.21 +1.2.643.2.2.22 mac89 GOST mac89 1.2.643.2.2.22 +1.2.643.2.9.1.6.1 param_encr_cc GOST param_encr_cc 1.2.643.2.9.1.6.1 +1.2.643.2.2.31.0 param_encr_cptest GOST param_encr_cptest 1.2.643.2.2.31.0 +1.2.643.2.2.31.1 param_encr_cpa GOST param_encr_cpa 1.2.643.2.2.31.1 +1.2.643.2.2.31.2 param_encr_cpb GOST param_encr_cpb 1.2.643.2.2.31.2 +1.2.643.2.2.31.3 param_encr_cpc GOST param_encr_cpc 1.2.643.2.2.31.3 +1.2.643.2.2.31.4 param_encr_cpd GOST param_encr_cpd 1.2.643.2.2.31.4 +1.2.643.7.1.2.5.1.1 param_encr_tc GOST param_encr_tc 1.2.643.7.1.2.5.1.1 +1.2.643.2.2.4 hash_with_sign94_cp GOST hash_with_sign94_cp 1.2.643.2.2.4 +1.2.643.2.9.1.3.3 hash_with_sign94_cc GOST hash_with_sign94_cc 1.2.643.2.9.1.3.3 +1.2.643.2.2.3 hash_with_sign01_cp GOST hash_with_sign01_cp 1.2.643.2.2.3 +1.2.643.2.9.1.3.4 hash_with_sign01_cc GOST hash_with_sign01_cc 1.2.643.2.9.1.3.4 +1.2.643.7.1.1.3.2 hash_with_sign12_256 GOST hash_with_sign12_256 1.2.643.7.1.1.3.2 +1.2.643.7.1.1.3.3 hash_with_sign12_512 GOST hash_with_sign12_512 1.2.643.7.1.1.3.3 +1.2.643.2.2.20 pk_sign94_cp GOST pk_sign94_cp 1.2.643.2.2.20 +1.2.643.2.9.1.5.3 pk_sign94_cc GOST pk_sign94_cc 1.2.643.2.9.1.5.3 +1.2.643.2.2.19 pk_sign01_cp GOST pk_sign01_cp 1.2.643.2.2.19 +1.2.643.2.9.1.5.4 pk_sign01_cc GOST pk_sign01_cc 1.2.643.2.9.1.5.4 +1.2.643.7.1.1.1.1 pk_sign12_256 GOST pk_sign12_256 1.2.643.7.1.1.1.1 +1.2.643.7.1.1.1.2 pk_sign12_512 GOST pk_sign12_512 1.2.643.7.1.1.1.2 +1.2.643.2.2.32.2 param_pubkey94_cpa GOST param_pubkey94_cpa 1.2.643.2.2.32.2 +1.2.643.2.2.32.3 param_pubkey94_cpb GOST param_pubkey94_cpb 1.2.643.2.2.32.3 +1.2.643.2.2.32.4 param_pubkey94_cpc GOST param_pubkey94_cpc 1.2.643.2.2.32.4 +1.2.643.2.2.32.5 param_pubkey94_cpd GOST param_pubkey94_cpd 1.2.643.2.2.32.5 +1.2.643.2.2.33.1 param_pubkey94_cpxcha GOST param_pubkey94_cpxcha 1.2.643.2.2.33.1 +1.2.643.2.2.33.2 param_pubkey94_cpxchb GOST param_pubkey94_cpxchb 1.2.643.2.2.33.2 +1.2.643.2.2.33.3 param_pubkey94_cpxchc GOST param_pubkey94_cpxchc 1.2.643.2.2.33.3 +1.2.643.2.2.35.0 param_pubkey01_cptest GOST param_pubkey01_cptest 1.2.643.2.2.35.0 +1.2.643.2.2.35.1 param_pubkey01_cpa GOST param_pubkey01_cpa 1.2.643.2.2.35.1 +1.2.643.2.2.35.2 param_pubkey01_cpb GOST param_pubkey01_cpb 1.2.643.2.2.35.2 +1.2.643.2.2.35.3 param_pubkey01_cpc GOST param_pubkey01_cpc 1.2.643.2.2.35.3 +1.2.643.2.2.36.0 param_pubkey01_cpxcha GOST param_pubkey01_cpxcha 1.2.643.2.2.36.0 +1.2.643.2.2.36.1 param_pubkey01_cpxchb GOST param_pubkey01_cpxchb 1.2.643.2.2.36.1 +1.2.643.2.9.1.8.1 param_pubkey01_cc GOST param_pubkey01_cc 1.2.643.2.9.1.8.1 +1.2.643.7.1.2.1.2.0 param_pubkey12_512_0 GOST param_pubkey12_512_0 1.2.643.7.1.2.1.2.0 +1.2.643.7.1.2.1.2.1 param_pubkey12_512_A GOST param_pubkey12_512_A 1.2.643.7.1.2.1.2.1 +1.2.643.7.1.2.1.2.2 param_pubkey12_512_B GOST param_pubkey12_512_B 1.2.643.7.1.2.1.2.2 + diff --git a/tcl_tests/opnssl.sh b/tcl_tests/opnssl.sh new file mode 100755 index 0000000..e51726d --- /dev/null +++ b/tcl_tests/opnssl.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +export HSTNME=`hostname` + +if test $HSTNME = tls-ref-cp10; then ossl=/usr/bin/openssl; fi +if test $HSTNME = tls-ref-cp20; then ossl=/opt/cryptopack2/bin/openssl; fi +if test $HSTNME = tls-ref-cp21; then ossl=/opt/cryptopack2/bin/openssl; fi + +$ossl $* + +exit $? diff --git a/tcl_tests/ossltest.tcl b/tcl_tests/ossltest.tcl new file mode 100644 index 0000000..c02d566 --- /dev/null +++ b/tcl_tests/ossltest.tcl @@ -0,0 +1,1052 @@ +# +# Расширение пакета test для OpenSSL +# +package require http +# Путь поиска пакета test +if {[info exists env(TOOLDIR)]} { + lappend auto_path $env(TOOLDIR) +} { + lappend auto_path "[file dirname [info script]]/../../maketool" +} + + +# outputs specified environment variables into log + +proc log_vars {args} { + foreach var $args { + if [info exists ::env($var)] { + log $var=$::env($var) + } else { + log "$var is not set" + } + } +} +# Проверка наличия необходимых переменных окружения +foreach var {OPENSSL_APP} { +if {![info exists env($var)]} { + puts stderr "Environment variable $var not defined" + exit 100 +} else { + set $var [file normalize $env($var)] +} +} + +if {[info exists env(OPENSSL_CONF)]} { + set OPENSSL_CONF $env(OPENSSL_CONF) +} else { + if {[regexp {OPENSSLDIR: "([^\"]+)"} [exec $OPENSSL_APP version -d] => openssl_dir]} { + set OPENSSL_CONF $openssl_dir/openssl.cnf + } else { + puts stderr "Cannot find out default openssl config" + exit 100 + } +} + +if {![file exists $OPENSSL_CONF]} { + puts "Configuration file $OPENSSL_CONF doesn't exist" + exit 100 +} + +if {$::tcl_platform(platform) != "windows"} { + proc kill {signal pid} { + exec kill -$signal $pid + } +} else { + proc kill {signal pid} { + exec taskkill /pid $pid /f + } +} + +package require test +set test::suffix "" +package require base64 + +# +# set up test::dir variable +# + +if {[info exists env(TESTDIR)]} { + set ::test::dir [file normalize $env(TESTDIR)] +} else { + set ::test::dir [file join [pwd] z] +} + +# +# Фильтрует вывод полученный в виде длинной строки, разбивая на строки +# по \n. Возвращает строки, удовлетворяющие регулярному выражениу +# pattern +# + +proc grep {pattern data} { + set out "" + foreach line [split $data "\n"] { + if {[regexp $pattern $line]} { + append out $line "\n" + } + } + return $out +} +proc check_builtin_engine {} { + global OPENSSL_APP + set found [regexp Cryptocom [exec $OPENSSL_APP engine 2> /dev/null]] + if {$found} { + puts "Using statically compiled engine" + } else { + puts "Using dynamically loaded engine" + } + return $found +} + + +# Вызывает команду openssl. +# Посылает в лог вывод на stdout и на stderr, возвращает его же. +proc openssl {cmdline} { + global ENGINE_PATH OPENSSL_APP + log_vars OPENSSL_CONF CRYPT_PARAMS RNG RNG_PARAMS CCENGINE_LICENSE + if {[info exists ::test::engine]} { + set cmdline [concat [lrange $cmdline 0 0] [list -engine $::test::engine] [lrange $cmdline 1 end]] + } + log "OpenSSL cmdline: $OPENSSL_APP $cmdline" + set f [open "|$OPENSSL_APP $cmdline" r] + set output [read $f] + if {[catch {close $f} msg]} { + append output "STDERR CONTENTS:\n$msg" + log $output + if {[lindex $::errorCode 0]!="NONE"} { + return -code error -errorcode $::errorCode $output + } + } + return $output +} + + +proc getConfig {args} { + global OPENSSL_CONF + if {![info exists OPENSSL_CONF]} { + if {![regexp "OPENSSLDIR: \"\[^\"\]+\"" [openssl version -d] => openssl_dir]} { + puts stderr "Cannot find out openssl directory" + exit 1 + } + set OPENSSL_CONF "$openssl_dir/openssl.cnf" + } + set f [open $OPENSSL_CONF r] + set out "" + set mode copy + while {[gets $f line]>=0} { + if {[regexp "\\s*\\\[\\s*(\\S+)\\s*\\\]" $line => section]} { + if {[lsearch -exact $args $section]!=-1} { + set mode skip + } else { + set mode copy + } + } + if {$mode eq "copy"} { + append out $line \n + } + } + return $out +} +# +# Создает тестовый CA +# Допустимые параметры: +# CAname - директория, в которой создается CA (testCA по умолчанию) +# алгоритм с параметрами в формате команды req +# + +proc makeCA {{CAname {}} {algor_with_par gost2012_512:A}} { + global OPENSSL_CONF + if {![string length $CAname]} { + set CAname [file rootname [file tail $::argv0]]CA-2012 + } + set test::ca $CAname + file delete -force $CAname + file mkdir $CAname + makeFile $CAname/ca.conf " +\[ ca \] +default_ca = CA_default # The default ca section + +\[ CA_default \] + +dir = [file join [pwd] $CAname] # top dir +database = \$dir/index.txt # index file. +new_certs_dir = \$dir/newcerts # new certs dir + +certificate = \$dir/cacert.pem # The CA cert +serial = \$dir/serial # serial no file +private_key = \$dir/private/cakey.pem# CA private key +RANDFILE = \$dir/private/.rand # random number file + +default_days = 3650 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use digest corresponding the algorithm +default_startdate = 060101000000Z + +policy = policy_any # default policy +email_in_dn = yes # add the email into cert D + + +nameopt = ca_default # Subject name display option +certopt = ca_default # Certificate display option +copy_extensions = copy # Copy extensions from requ + + +\[ policy_any \] +countryName = supplied +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = supplied + +" + makeFile $CAname/req.conf " +\[req\] +prompt=no +distinguished_name = req_dn +\[ req_dn \] +C = RU +L = Moscow +CN=Test CA $algor_with_par +O=Cryptocom +OU=OpenSSL CA +emailAddress = openssl@cryptocom.ru +" + file mkdir $CAname/private + file mkdir $CAname/newcerts + generate_key [keygen_params $algor_with_par] $CAname/private/cakey.pem + openssl "req -new -x509 -key $CAname/private/cakey.pem -nodes -out $CAname/cacert.pem -config $CAname/req.conf -set_serial 0x11E" + makeFile ./$CAname/.rand 1234567890 + makeFile ./$CAname/serial 011E + makeFile ./$CAname/index.txt "" + return [file isfile $CAname/cacert.pem] +} + +proc extract_oids {filename {format PEM} {offset 0}} { + set out "" + if {$offset} { + set miscargs "-offset $offset " + } else { + set miscargs "" + } + foreach line [split [openssl "asn1parse $miscargs-in $filename -inform $format -oid oidfile"] "\n"] { + if {([regexp {Gost\d+} $line]||[regexp "GostR" $line]||[regexp "GOST" $line]||[regexp "sha1" $line]) && ![regexp ^Loaded: $line]} { + regsub {[^:]+:[^:]+:} $line "" line + append out $line "\n" + } + } + return $out +} +# +# Формирует список параметров для openssl req необходимый для формирования +# ключа c указанным алгоритмом и параметрами +# +proc keygen_params {alg} { + return [split $alg :] +} + +proc generate_key {params filename} { + set alg [lindex $params 0] + set param [lindex $params 1] + set keyname $alg + set keyname [append keyname _ $param .pem] + switch -glob $alg { + rsa { + if {![string length $param]} { + set param 1024 + set keyname "rsa_1024.pem" + } + set optname "-algorithm rsa -pkeyopt rsa_keygen_bits:$param" + } + ec {set optname "-paramfile $param"} + dsa {set optname "-paramfile $param" } + gost* { set optname "-algorithm $alg -pkeyopt paramset:$param" } + } + if {$::tcl_platform(platform) eq "windows"} { + set exesuffix ".exe" + } else { + set exesuffix "" + } + log "Keyname is $keyname" +# if {[engine_name] eq "open"} { + log "Calling openssl cmd to create private key" + openssl "genpkey $optname -out $filename" +# } elseif {[info exists ::env(OBJ)] && [file executable ../$::env(OBJ)/keytest$exesuffix]&& $alg eq "gost2001"} { +# log "keytest$exesuffix $alg $param $filename" +# exec ../$::env(OBJ)/keytest$exesuffix $alg $param $filename >&@ stdout +# } elseif {[info exists ::env(OBJ)] && [file executable ../$::env(OBJ)/keytest$exesuffix]&& $alg eq "gost2012_256"} { +# log "keytest$exesuffix $alg $param $filename" +# exec ../$::env(OBJ)/keytest$exesuffix $alg $param $filename >&@ stdout +# } elseif {[info exists ::env(OBJ)] && [file executable ../$::env(OBJ)/keytest$exesuffix]&& $alg eq "gost2012_512"} { +# log "keytest$exesuffix $alg $param $filename" +# exec ../$::env(OBJ)/keytest$exesuffix $alg $param $filename >&@ stdout +# } elseif {[info exists ::env(PRIVATEKEYSDIR)] && [file exists $::env(PRIVATEKEYSDIR)/$keyname]} { +# log "Copying file $keyname" +# file copy $::env(PRIVATEKEYSDIR)/$keyname $filename +# } else { +# log "Calling openssl cmd to create private key" +# openssl "genpkey $optname -out $filename" +# } +} + +# +# Создает тестового пользователя с одним ключом подписи и одной заявкой +# на сертификат. +# Параметры +# username Имя директории, куда складывать файлы этого пользователя +# alg Параметр для опции -newkey команды openssl req, задающий алгоритм +# ключа и параметры этого алгоритма +# Последующие параметры имеют вид списка ключ значение и задают поля +# Distinguished Name +# FIXME Процедуру надо поправить, чтобы работала с новой версией openssl +proc makeUser {username alg args} { + file delete -force $username + file mkdir $username + if {[lsearch $args CN]==-1} { + lappend args CN $username + } + makeFile $username/req.conf [eval makeConf $args] + log "req.conf --------\n[getFile $username/req.conf]-------------" + + generate_key [keygen_params $alg] $username/seckey.pem + openssl "req -new -key $username/seckey.pem -nodes -out $username/req.pem -config $username/req.conf" + return [expr {[file size $username/req.pem] > 0}] +} + +proc makeSecretKey {username alg} { + file delete -force $username + file mkdir $username + generate_key [keygen_params $alg] $username/seckey.pem + return [expr {[file size $username/seckey.pem] > 0}] +} + +# +# Создает пользователя с помощью makeUser и подписывает его сертификат +# ключом ранее созданного testCA. +# Параметр CAname обрабатывается специальным образом: он не попадает в DN +# +proc makeRegisteredUser {username alg args } { + if {![info exists params(CAname)]&&![info exists ::test::ca]} { + return -code error "Default CA name is not known. Have you called makeCA earlier in this script?" + } + set CAname $test::ca + array set params $args + if {[info exist params(CAname)]} { + set CAname $params(CAname) + unset params(CAname) + } + if {![file isdirectory $CAname]||![file exists $CAname/cacert.pem]} { + return -code error "CA $CAname doesn't exists" + } + eval makeUser [list $username $alg] [array get params] + openssl "ca -config $CAname/ca.conf -in $username/req.pem -out $username/cert.pem -batch -notext" + return [file isfile $username/cert.pem] +} + +proc makeConf {args} { + global OPENSSL_CONF + array set dn_attrs [list C RU\ + L Moscow\ + CN "Dummy user"\ + O Cryptocom\ + OU "OpenSSL Team"\ + emailAddress "openssl@cryptocom.ru"\ + ] + array set dn_attrs $args + if {[info exists dn_attrs(extensions)]} { + set extensions $dn_attrs(extensions) + unset dn_attrs(extensions) + } + set out "" + append out {[req] +prompt=no +distinguished_name = req_dn +} +if {[info exists extensions]} { + append out "req_extensions = req_exts\n\[ req_exts \]\n" $extensions "\n" +} +append out "\[ req_dn \]\n" + foreach {key val} [array get dn_attrs] { + append out "$key=$val\n" + } + return $out +} +# +# Выполняет замену регулярного выражения re на строку s в указанном +# PEM-документе. +# +proc hackPem {re pem s} { + set out "" + foreach {whole_pem start_line coded_body end_line} [regexp -inline -all "(-----BEGIN \[^\n\]+-----\n)(.*?)(\n-----END \[^\n\]+-----\n)" $pem] { + set der [::base64::decode $coded_body] + set der [regsub -all $re $der $s] + append out $start_line [::base64::encode $der] $end_line + } + return $out +} + +# +# Handling of OIDs +# + +source [file dirname [info script]]/name2oid.tcl +foreach {name oid} [array get name2oid] { + set oid2name($oid) $name +} + +proc long_name_by_id {id} { + variable name2oid + variable oid2name + if {[regexp {^\d+(\.\d+)+$} $id]} { + return "GOST $oid2name($id) $id" + } + return "GOST $id $name2oid($id)" +} + +proc alg_id {alg} { + switch -glob $alg { + gost94cc {return pk_sign94_cc} + gost94cc:* {return pk_sign94_cc} + gost94:* {return pk_sign94_cp} + gost2001cc:* {return pk_sign01_cc} + gost2001cc {return pk_sign01_cc} + gost2001:* {return pk_sign01_cp} + gost2012_256:* {return pk_sign12_256} + gost2012_512:* {return pk_sign12_512} + } +} + +proc alg_with_digest {alg} { + variable name2oid + switch -glob $alg { + gost94cc {return hash_with_sign94_cc} + gost94cc:* {return hash_with_sign94_cc} + gost94:* {return hash_with_sign94_cp} + gost2001cc:* {return hash_with_sign01_cc} + gost2001cc {return hash_with_sign01_cc} + gost2001:* {return hash_with_sign01_cp} + gost2012_256:* {return hash_with_sign12_256} + gost2012_512:* {return hash_with_sign12_512} + + } +} + +proc alg_long_name {alg} { + variable name2oid + switch -glob $alg { + #gost94cc {return hash_with_sign94_cc} + #gost94cc:* {return hash_with_sign94_cc} + #gost94:* {return hash_with_sign94_cp} + #gost2001cc:* {return hash_with_sign01_cc} + #gost2001cc {return hash_with_sign01_cc} + gost2001:* {return "GOST R 34.10-2001"} + gost2012_256:* {return "GOST R 34.10-2012 with 256 bit modulus"} + gost2012_512:* {return "GOST R 34.10-2012 with 512 bit modulus"} + } +} + +# Returns hash algorithm corresponded to sign algorithm +proc alg_hash {alg} { + switch -glob $alg { + gost2012_256:* {return hash_12_256} + gost2012_512:* {return hash_12_512} + * {return hash_94} + } +} + +# Returns short name of hash algorithm +proc hash_short_name {hash_alg} { + switch -glob $hash_alg { + *hash_94 {return md_gost94} + hash_12_256 {return md_gost12_256} + hash_12_512 {return md_gost12_512} + default {return $hash_alg} + } +} + +proc ts_hash_long_name {hash_alg} { + switch -glob $hash_alg { + *hash_94 {return md_gost94} + hash_12_256 {return md_gost12_256} + hash_12_512 {return md_gost12_512} + default {return $hash_alg} + } +} + +# Returns long name of hash algorithm +proc hash_long_name {hash_alg} { + switch -glob $hash_alg { + *hash_94* {return "GOST R 34.11-94"} + gost2001* {return "GOST R 34.11-94"} + *12_256* {return "GOST R 34.11-2012 with 256 bit hash"} + *12_512* {return "GOST R 34.11-2012 with 512 bit hash"} + default {return $hash_alg} + } +} + +# Returns long name of hash_with_sign algorithm +proc hash_with_sign_long_name {alg} { + switch -glob $alg { + gost2001:* {return "GOST R 34.11-94 with GOST R 34.10-2001"} + gost2012_256:* {return "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)"} + gost2012_512:* {return "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)"} + default {return $alg} + } +} + +proc smime_hash_with_sign_long_name {alg} { + switch -glob $alg { + hash_with_sign01_cp {return "GOST R 34.11-94 with GOST R 34.10-2001"} + hash_with_sign12_256 {return "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)"} + hash_with_sign12_512 {return "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)"} + default {return $alg} + } +} + +proc micalg {hash_alg} { + switch -exact $hash_alg { + hash_94 {return "gostr3411-94"} + hash_12_256 {return "gostr3411-2012-256"} + hash_12_512 {return "gostr3411-2012-512"} + } +} + +proc param_pubkey {alg} { + variable name2oid + switch -exact $alg { + gost94cc: {return param_pubkey94_cpa} + gost94cc {return param_pubkey94_cpa} + gost94:A {return param_pubkey94_cpa} + gost94:B {return param_pubkey94_cpb} + gost94:C {return param_pubkey94_cpc} + gost94:D {return param_pubkey94_cpd} + gost94:XA {return param_pubkey94_cpxcha} + gost94:XB {return param_pubkey94_cpxchb} + gost94:XC {return param_pubkey94_cpxchc} + gost2001cc: {return param_pubkey01_cc} + gost2001cc {return param_pubkey01_cc} + gost2001:0 {return param_pubkey01_cptest} + gost2001:A {return param_pubkey01_cpa} + gost2001:B {return param_pubkey01_cpb} + gost2001:C {return param_pubkey01_cpc} + gost2001:XA {return param_pubkey01_cpxcha} + gost2001:XB {return param_pubkey01_cpxchb} + gost2012_256:0 {return param_pubkey01_cptest} + gost2012_256:A {return param_pubkey01_cpa} + gost2012_256:B {return param_pubkey01_cpb} + gost2012_256:C {return param_pubkey01_cpc} + gost2012_256:XA {return param_pubkey01_cpxcha} + gost2012_256:XB {return param_pubkey01_cpxchb} + gost2012_512:0 {return param_pubkey12_512_0} + gost2012_512:A {return param_pubkey12_512_A} + gost2012_512:B {return param_pubkey12_512_B} + } +} + + +proc param_hash_long_name {hash_alg} { + switch -glob $hash_alg { + *hash_94 {return "id-GostR3411-94-CryptoProParamSet"} + hash_12_256 {return "GOST R 34.11-2012 with 256 bit hash"} + hash_12_512 {return "GOST R 34.11-2012 with 512 bit hash"} + } +} + +proc pubkey_long_name {alg} { + variable name2oid + switch -glob $alg { + + #gost2001cc: {return param_pubkey01_cc} + #gost2001cc {return param_pubkey01_cc} + #gost2001:0 {return param_pubkey01_cptest} + gost2001:A {return "id-GostR3410-2001-CryptoPro-A-ParamSet"} + gost2001:B {return "id-GostR3410-2001-CryptoPro-B-ParamSet"} + gost2001:C {return "id-GostR3410-2001-CryptoPro-C-ParamSet"} + gost2001:XA {return "id-GostR3410-2001-CryptoPro-XchA-ParamSet"} + gost2001:XB {return "id-GostR3410-2001-CryptoPro-XchB-ParamSet"} + #gost2012_256:0 {return param_pubkey01_cptest} + gost2012_256:A {return "id-GostR3410-2001-CryptoPro-A-ParamSet"} + gost2012_256:B {return "id-GostR3410-2001-CryptoPro-B-ParamSet"} + gost2012_256:C {return "id-GostR3410-2001-CryptoPro-C-ParamSet"} + gost2012_256:XA {return "id-GostR3410-2001-CryptoPro-XchA-ParamSet"} + gost2012_256:XB {return "id-GostR3410-2001-CryptoPro-XchB-ParamSet"} + #gost2012_512:0 {return param_pubkey12_512_0} + gost2012_512:A {return "GOST R 34.10-2012 (512 bit) ParamSet A"} + gost2012_512:B {return "GOST R 34.10-2012 (512 bit) ParamSet B"} + } +} + + + +proc mkObjList {args} { + set out "" + foreach name $args { + append out " OBJECT :$name\n" + } + return $out +} + +proc structured_obj_list {args} { + variable name2oid + set out {} + foreach {path name} $args { + if {$name != {}} {set oid $name2oid($name)} {set oid {}} + lappend out "$path=$oid" + } + return $out +} + +proc param_hash {alg} { + switch -glob $alg { + gost2012_256:* {return hash_12_256} + gost2012_512:* {return hash_12_512} + * {return param_hash_94} + } +} + + +proc param_encr {short_name} { + variable name2oid + if {[regexp {^\d+(\.\d+)+$} $short_name]} { + return "$short_name" + } + switch -exact $short_name { + cc_cipher_param {return param_encr_cc} + {} {return param_encr_tc} + cp_cipher_param_a {return param_encr_cpa} + cp_cipher_param_b {return param_encr_cpb} + cp_cipher_param_c {return param_encr_cpc} + cp_cipher_param_d {return param_encr_cpd} + } +} + +proc encr_long_name {short_name} { + variable name2oid + switch -exact $short_name { + "1.2.643.2.2.31.1" {return "id-Gost28147-89-CryptoPro-A-ParamSet"} + "1.2.643.2.2.31.2" {return "id-Gost28147-89-CryptoPro-B-ParamSet"} + "1.2.643.2.2.31.3" {return "id-Gost28147-89-CryptoPro-C-ParamSet"} + "1.2.643.2.2.31.4" {return "id-Gost28147-89-CryptoPro-D-ParamSet"} + "1.2.643.7.1.2.5.1.1" {return "GOST 28147-89 TC26 parameter set"} + {} {return "GOST 28147-89 TC26 parameter set"} + } +} + + + +# +# Функции для управления клиентом и сервером при тестировании +# SSL-соединения +# + +# Параметры +# Список аргументов командной строки клиента +# список аргументов командной строки сервера +# строка, которую надо передать на stdin клиенту +# +# Запускает openssl s_server и пытается приконнектиться к нему openssl +# s_client-ом. Возвращает список stdout клиента, stderr клиента, кода +# завершения клиента, stdout +# сервера stderr сервера и кода завершения сервера. +# +# Если процесс убит сигналом, возвращает в качестве кода завершения имя +# сигнала, иначе - числовое значение кода завершения ОС +# +proc client_server {client_args server_args client_stdin} { + log "CLIENT ARGS\n$client_args\n" + log "SERVER ARGS\n$server_args\n" + flush [test_log] + set server [open_server $server_args] + set client [open_client $client_args $client_stdin] + log "server = $server client = $client" + log "Both client and server started" + flush [test_log] + global finished + log "Waitng for client to termintate" + flush [test_log] +# if {$::tcl_platform(platform) == "windows"} { +# exec ../kbstrike [pid $client] 0x20 +# } + vwait finished($client) + catch {stop_server $server} + set list [concat [stop $client] [stop $server]] + foreach channel {"CLIENT STDOUT" "CLIENT STDERR" "CLIENT EXIT CODE" "SERVER STDOUT" + "SERVER STDERR" "SERVER EXIT CODE"} data $list { + log "$channel\n$data\n" + } + return $list +} +# +# Устанавливает командную строку для вызова клиента, +# в системный openssl на указанном хосте +# +proc remote_client {host} { + if {[info hostname] == "$host"} { + set ::test::client_unset {OPENSSL_CONF} + set ::test::client_app "openssl s_client" + } else { + set ::test::client_unset {LD_LIBRARY_PATH OPENSSL_CONF} + set ::test::client_app "ssh build@$host openssl s_client" + } +} +# +# Устанавливает командную строку для вызова клиента в указанную команду +# Необязательный параметр указывает список переменных окружения, которые +# НЕ НАДО передавать в эту команду +# +proc custom_client {command {forbidden_vars {}}} { + set ::test::client_app $command + set ::test::client_unset $forbidden_vars + +} +# +# Восстанавливает станадртую клиентскую команду +# +proc our_client {} { + catch {unset ::test::client_app} + catch {unset ::test::client_unset} +} + +# +# Закрывает файл, указанный в соответствующем file_id, возвращает +# элемент глобального массива output, содержимое error message от close +# и код завершения процесса (имя сигнала) +proc stop {file_id} { + global output + fconfigure $file_id -blocking yes + if {[catch {close $file_id} msg]} { + if {[string match CHILD* [lindex $::errorCode 0]]} { + set status [lindex $::errorCode 2] + } else { + set status 0 + } + } else { + set status 0 + } + return [list $output($file_id) $msg $status] +} +# +# Завершает работу сервера +# +proc stop_server {file_id} { +# puts $file_id "Q\n" +# catch {set xx [socket localhost 4433]} + log "Interrupting process [pid $file_id]" + flush [test_log] + kill INT [pid $file_id] + #puts -nonewline stderr "Waiting for server termination.." + vwait finished($file_id) + if [info exists xx] {close $xx} +# puts stderr "Ok" +} + +# +# Запускает процесс с указанной командной строкой. Возвращает дескриптор +# файла в nonblocking mode с повешенным туда fileevent +# Очищает соответствующие элементы массивов output и finished +proc start_process {cmd_line read_event {mode "r"}} { + set f [open "|$cmd_line" $mode] + global output finished + catch {unset finished($f)} + fconfigure $f -buffering none -blocking n + set output($f) "" + fileevent $f readable [list $read_event $f] + return $f +} +# +# Обработчик fileevent-ов на чтение. Записывает считанные данные в +# элемент массива output соответствущий файлхендлу. В случае если +# достигнут eof, выставляет элемент массива finished. (элемент output +# при этом тоже трогается, чтобы vwait завершился) +# +proc process_read {f} { + global output + if {[eof $f]} { + global finished + fconfigure $f -blocking y + set finished($f) 1 + append output($f) "" + return + } + append output($f) [read $f] +} + +# +# Запускает openssl s_server с указанными аргументами и дожидается пока +# он скажет на stdout ACCEPT. Возвращает filehandle, открытый на +# чтение/запись +# +proc open_server {server_args} { + global OPENSSL_APP + global ENGINE_PATH + if {[info exists ::test::server_conf]} { + global env + set save_conf $env(OPENSSL_CONF) + set env(OPENSSL_CONF) $::test::server_conf + } + if {[info exists ::test::server_app]} { + set server $::test::server_app + } else { + set server [list $OPENSSL_APP s_server] + } + if {[info exists ::test::server_unset]} { + save_env $::test::server_unset + } + set server [start_process [concat $server $server_args] process_read "r+"] + restore_env + if {[info exists save_conf]} { + set env(OPENSSL_CONF) $save_conf + } + + global output finished + #puts -nonewline stderr "Waiting for server startup..." + while {![regexp "\nACCEPT\n" $output($server)]} { + vwait output($server) + if {[info exists finished($server)]} { + #puts stderr "error" + return -code error [lindex [stop $server] 1] + } + } + #puts stderr "Ok" + after 100 + return $server +} +# +# Сохраняет указанные переменные среды для последующего восстановления +# restore_env +# +proc save_env {var_list} { + catch {array unset ::test::save_env} + foreach var $var_list { + if {[info exist ::env($var)]} { + set ::test::save_env($var) $::env($var) + unset ::env($var) + } + } + +} +proc restore_env {} { + if {[array exists ::test::save_env]} { + array set ::env [array get ::test::save_env] + array unset ::test::save_env + } + +} +# +# Сохраняет указанные переменные среды для последующего восстановления +# restore_env2. В отличие от save_env, не делает unset сохраненной переменной. +# +proc save_env2 {var_list} { + catch {array unset ::test::save_env2} + foreach var $var_list { + if {[info exist ::env($var)]} { + set ::test::save_env2($var) $::env($var) + } + } + +} +# +# Восстанавливает переменные среды, ранее сохраненные функцией save_env2 +# В отличие от функции restore_env, требует списка переменных и +# восстанавливает только переменные из данного списка. Второе отличие - +# если переменная из списка не была сохранена, делает ей unset. +# +proc restore_env2 {var_list} { + foreach var $var_list { + if {[info exist ::test::save_env2($var)]} { + set ::env($var) $::test::save_env2($var) + } else { + catch {unset ::env($var)} + } + } + array unset ::test::save_env2 +} + + +# +# Запускает s_client с указанными аргументами, передавая на stdin +# указанную строку +# +proc open_client {client_args client_stdin} { + global OPENSSL_APP + if [info exists ::test::client_app] { + set client $::test::client_app + } else { + set client [list $OPENSSL_APP s_client] + } + if {[info exists ::test::client_unset]} { + save_env $::test::client_unset + } + if {[info exists ::test::client_conf]} { + set save_env(OPENSSL_CONF) $::env(OPENSSL_CONF) + set ::env(OPENSSL_CONF) $::test::client_conf + } + set client [start_process [concat $client $client_args [list << $client_stdin]] process_read] + restore_env + return $client +} +# +# Зачитывает список хостов из ../../ssl-ciphers +# +proc get_hosts {file} { + set ::test::suffix "-$file" + if [file readable $file.ciphers] { + set f [open $file.ciphers] + } else { + set f [open ../../ssl-ciphers/$file.ciphers r] + } + while {[gets $f line]>=0} { + if {[regexp {^\s*#} $line]} continue + append data "$line\n" + } + close $f + global hosts + array set hosts $data +} +# +# Регистрирует пользователся (возможно удаленном) тестовом CA, используя +# скрипт testca установленный в PATH на CAhost. +# + +proc registerUserAtCA {userdir CAhost CAprefix CApath} { + global OPENSSL_APP + log "registerUserAtCA $userdir $CAhost $CAprefix $CApath" + set f [open $userdir/req.pem] + set request [read $f] + close $f + set token [::http::geturl http://$CAhost/$CAprefix/$CApath\ + -query [::http::formatQuery request $request startdate [clock\ + format [expr [clock seconds]-3600] -format "%y%m%d%H%M%SZ" -gmt y]]] + if {[::http::ncode $token]!=200} { + return -code error "Error certifying request [::http::data $token]" + } + log "Got a certificate. Saving" + saveCertFromPKCS7 $userdir/cert.pem [::http::data $token] +} +proc saveCertFromPKCS7 {file pkcs7} { + global OPENSSL_APP + log saveCertFromPCS7 + log "$OPENSSL_APP pkcs7 -print_certs $pkcs7" + set f [open "|[list $OPENSSL_APP pkcs7 -print_certs << $pkcs7]" r] + set out [open $file w] + set mode 0 + while {[gets $f line]>=0} { + if {$mode==1} { + puts $out $line + if {$line eq "-----END CERTIFICATE-----"} { + set mode 2 + } + } elseif {$mode==0 && $line eq "-----BEGIN CERTIFICATE-----"} { + set mode 1 + puts $out $line + } + } + close $f + close $out + if {$mode !=2 } { + return -code error "Cannot get certificate from PKCS7 output" + } +} +# +# Invokes scp and discards stderr output if exit code is 0 +# +proc scp {args} { + if {[info exists env(SCP)]} { + set scp $env(SCP) + } else { + set scp scp + } + if {[catch [concat exec $scp $args] msg]} { + if {[string match CHIDLD* [lindex $::errorCode 0]]} { + return -code error -errorcode $::errorCode $msg + } + } +} + +proc getCAAlgParams {CAhost CAprefix alg} { + if {$alg == "ec" || $alg == "dsa"} { + set token [::http::geturl http://$CAhost/$CAprefix/$alg?algparams=1] + if {[::http::ncode $token]!=200} { + return -code error "Error getting algorithm parameters [::http::data $token]" + } + set f [open ${alg}params.pem w] + puts $f [::http::data $token] + close $f + } +} +# +# Copies CA certificate from specified CA into ca_$alg.pem +# Returns name of the ca certificate or empty line if something goes +# wrong and error wasn't properly detected +# +proc getCAcert {CAhost CApath alg} { + set token [::http::geturl http://$CAhost$CApath/$alg?getroot=1] + if {[::http::ncode $token]!=200} { + return -code error "Error getting root cert for $alg: [::http::data $token]" + } + saveCertFromPKCS7 ca_$alg.pem [::http::data $token] + return ca_$alg.pem +} +# +# Returns decoded version of first pem object in the given file +# +proc readpem {filename} { + set f [open $filename] + fconfigure $f -translation binary + set data [read $f] + close $f + if {[regexp -- "-----BEGIN \[^\n\]+-----\r?\n(.*\n)-----END" $data => b64]} { + set data [::base64::decode $b64] + } + return $data + +} + +proc der_from_pem {pem} { + if {[regexp -- {^-----BEGIN ([^\n]*)-----\r?\n(.*)\r?\n-----END \1-----} $pem => => base64]} { + ::base64::decode $base64 + } { + error "Not a PEM:\n$pem" + } +} + +proc engine_name {} { + global env + if {[info exists env(ENGINE_NAME)]} { + switch -exact $env(ENGINE_NAME) { + "open" {return "open"} + "gost" {return "open"} + "cryptocom" {return "ccore"} + "ccore" {return "ccore"} + default {error "Unknown engine '$env(ENGINE_NAME)'"} + } + } else { + return "ccore" + } +} + +proc openssl_remote {files host cmdlinex suffix} { + set hostname [exec hostname] + set workpath /tmp/$hostname/$suffix + save_env {LD_LIBRARY_PATH OPENSSL_CONF ENGINE_DIR} + exec ssh build@$host mkdir -p $workpath + foreach file $files { + exec scp -r $file build@$host:$workpath + } + exec scp ../opnssl.sh build@$host:$workpath + exec ssh build@$host chmod +x $workpath/opnssl.sh + set cmdline [string map "TESTPATH $workpath" $cmdlinex] + log "hstname: $hostname OpenSSL cmdline: $host remote_openssl $cmdline" + set f [open "| ssh build@$host $workpath/opnssl.sh $cmdline" r] + set output [read $f] + restore_env + if {[catch {close $f} msg]} { + append output "STDERR CONTENTS:\n$msg" + log $output + if {[lindex $::errorCode 0]!="NONE"} { + return -code error -errorcode $::errorCode $output + } + } + return $output +} + +package provide ossltest 0.7 diff --git a/tcl_tests/pkcs12.try b/tcl_tests/pkcs12.try new file mode 100644 index 0000000..52e23d6 --- /dev/null +++ b/tcl_tests/pkcs12.try @@ -0,0 +1,55 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir + +start_tests "Тесты на команду pkcs12" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2001:XA gost2001:XB gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_pkcs12_$alg_fn + switch -glob $alg { + gost2012_512:* {set hash_alg md_gost12_512} + gost2012_256:* {set hash_alg md_gost12_256} + default {set hash_alg md_gost94} + } + +test -createsfiles [list $username/sscert.pem $username/sskey.pem]\ + "Генерируем сертификат и секретный ключ $alg" { + makeSecretKey $username $alg + makeFile $username/req.conf [makeConf] + openssl "req -new -x509 -config $username/req.conf -key $username/seckey.pem -out $username/cert.pem" + expr {[file size $username/cert.pem] > 0} +} 0 1 + +test -createsfiles {$username/pkcs12.p12} "Собираем pkcs12 с алгоритмом $alg" { + openssl "pkcs12 -export -inkey $username/seckey.pem -in $username/cert.pem -out $username/pkcs12.p12 -password pass:12345 -keypbe gost89 -certpbe gost89 -macalg $hash_alg" + file exists $username/pkcs12.p12 +} 0 1 + +test -skip {![file exists $username/pkcs12.p12]} -createsfiles [list $username/extracted_cert.pem $username/extracted_key.pem] "Разбираем pkcs12 c алгоритмом $alg" { + openssl "pkcs12 -in $username/pkcs12.p12 -nodes -out $username/dump.pem -password pass:12345" + set dump [getFile $username/dump.pem] + set lextr [regexp -all -inline "\n-----BEGIN .*?\n-----END \[^\n\]+-----\n" $dump] + + list [llength $lextr] [expr {[lindex $lextr 0] eq "\n[getFile $username/cert.pem]"}] [expr {[lindex $lextr 1] eq "\n[openssl "pkcs8 -nocrypt -topk8 -in $username/seckey.pem"]"}] + +} 0 {2 1 1} + + +#./load_engine pkcs12 -export -in t/z/U_enc_gost94_/cert.pem -inkey t/z/U_enc_gost94_/seckey.pem -certfile t/z/testCA/cacert.pem -name "CERTIFICATE" -out mycert.p12 -password pass:12345 +#./load_engine pkcs12 -in mycert.p12 -out mycert.pem + +} + +end_tests diff --git a/tcl_tests/pkcs7.tcl b/tcl_tests/pkcs7.tcl new file mode 100644 index 0000000..4e9d134 --- /dev/null +++ b/tcl_tests/pkcs7.tcl @@ -0,0 +1,187 @@ +package require base64 +if {[info exists env(TOOLDIR)]} { + lappend auto_path $env(TOOLDIR) +} { + lappend auto_path "[file dirname [info script]]/../../maketool" +} +package require asn 0.7.1 + +namespace eval pkcs7 { + namespace import ::asn::* + namespace export * + + proc asnTag {data_var} { + upvar $data_var data + asnPeekByte data b + return $b + } + + proc envelopedData {der} { + asnGetSequence der seq0 + asnGetObjectIdentifier seq0 id_envelopedData + if {$id_envelopedData != {1 2 840 113549 1 7 3}} { + error "Waited id-envelopedData, got $id_envelopedData" + } + asnGetContext seq0 n envelopedData + if {$n != 0} { + error "Waited context 0, got $n" + } + asnGetSequence envelopedData envelopedData + asnGetInteger envelopedData version + set originatorInfo {} + if {[asnTag envelopedData] != 0x31} { + asnGetContext envelopedData tag originatorInfo + } + asnGetSet envelopedData recipientInfos + asnGetSequence envelopedData encryptedContentInfo + set unprotectedAttrs {} + if {[string length $envelopedData]} { + asnGetContext envelopedData tag unprotectedAttrs + } + return [list $version $originatorInfo $recipientInfos $encryptedContentInfo $unprotectedAttrs $envelopedData] + } + + proc recipientInfos {rIs} { + set result {} + while {[string length $rIs]} { + asnGetSequence rIs inf + asnGetInteger inf version + set tag {} + if {[asnTag inf] == 0x30} { + asnGetSequence inf rid + } { + asnGetContext inf tag rid + } + asnGetSequence inf keyEncAlg + asnGetOctetString inf encryptedKey + lappend result [list $version [list $tag $rid] $keyEncAlg $encryptedKey] + } + return $result + } + + proc subjectPublicKeyInfo {spki} { + asnGetSequence spki algorithmIdentifier + asnGetBitString spki subjectPublicKey + list $algorithmIdentifier $subjectPublicKey $spki + } + + proc algorithmIdentifier {ai} { + asnGetObjectIdentifier ai oid + set param {} + if {[string length $ai]} { + asnGetSequence ai param + } + return [list $oid $param $ai] + } + + proc algorithmParamPKGOST {param} { + asnGetObjectIdentifier param pubkey_param + asnGetObjectIdentifier param digest_param + set cipher_param {} + if {[string length $param]} { + asnGetObjectIdentifier param cipher_param + } + return [list $pubkey_param $digest_param $cipher_param $param] + } + + proc keyTransportGOST {octet_string} { + asnGetSequence octet_string inf + asnGetSequence inf encryptedKey + set transportParams {} + if {[string length $inf]} { + asnGetContext inf tag transportParams + } + return [list $encryptedKey $transportParams $inf] + } + + proc encryptedKeyGOST {encryptedKeyAndMAC} { + asnGetOctetString encryptedKeyAndMAC encryptedKey + asnGetOctetString encryptedKeyAndMAC MAC + list $encryptedKey $MAC $encryptedKeyAndMAC + } + + proc transportParameters {transportParams} { + asnGetObjectIdentifier transportParams encryptionParamSet + set ephemeralPublicKey {} + if {[asnTag transportParams] == 0xa0} { + asnGetContext transportParams tag ephemeralPublicKey + } + asnGetOctetString transportParams ukm + list $encryptionParamSet $ephemeralPublicKey $ukm $transportParams + } + + proc encryptedContentInfo {eci} { + asnGetObjectIdentifier eci oid + asnGetSequence eci algorithmIdentifier + set encryptedContent {} + if {[string length $eci]} { + asnGetContext eci tag encryptedContent + } + list $oid $algorithmIdentifier $encryptedContent $eci + } + + proc algorithmParamEncGOST {param} { + asnGetOctetString param ukm + asnGetObjectIdentifier param encParam + list $ukm $encParam $param + } + + proc algorithm_oids_from_envelopedData {der} { + set result {} + foreach {v oI rIs eCI uAs t} [envelopedData $der] { + # recipient infos + set rin 0 + foreach rI [recipientInfos $rIs] { + foreach {v rid kEA eK} $rI { + # export (pubkey) algorithm identifier + foreach {pk_oid param t} [algorithmIdentifier $kEA] { + lappend result ri${rin}:kea=[join $pk_oid .] + foreach {pkp dp cp t} [algorithmParamPKGOST $param] { + lappend result \ + ri${rin}:kea:pkp=[join $pkp .] \ + ri${rin}:kea:dp=[join $dp .] \ + ri${rin}:kea:cp=[join $cp .] + } + } + # encryptedKey encapsulated structure + foreach {eK tPs t} [keyTransportGOST $eK] { + # transport parameters + foreach {ePS ePK ukm t} [transportParameters $tPs] { + # encryption paramset + lappend result ri${rin}:ktcp=[join $ePS .] + # ephemeral public key + if {[string length $ePK]} { + foreach {aI sPK t} [subjectPublicKeyInfo $ePK] { + # algorithm identifier + foreach {pKI param t} [algorithmIdentifier $aI] { + lappend result ri${rin}:ktepk=[join $pKI .] + foreach {pkp dp cp t} [algorithmParamPKGOST $param] { + lappend result \ + ri${rin}:ktepk:pkp=[join $pkp .] \ + ri${rin}:ktepk:dp=[join $dp .] \ + ri${rin}:ktepk:cp=[join $cp .] + } + } + } + } + } + } + } + incr rin + } + foreach {oid aI eC t} [encryptedContentInfo $eCI] { + # algorithm identifier + foreach {oid param t} [algorithmIdentifier $aI] { + lappend result ea=[join $oid .] + foreach {ukm oid t} [algorithmParamEncGOST $param] { + lappend result ea:cp=[join $oid .] + } + } + } + } + return $result + } + +} + +package provide pkcs7 0.1 \ No newline at end of file diff --git a/tcl_tests/pkcs8.try b/tcl_tests/pkcs8.try new file mode 100644 index 0000000..ce742c7 --- /dev/null +++ b/tcl_tests/pkcs8.try @@ -0,0 +1,170 @@ +#!/usr/bin/tclsh +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "тесты на команду pkcs8" +set key "-----BEGIN PRIVATE KEY----- +MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgIgSZ82qYpu6RQj +UeoKl5svrvYuMriHeAQvuSIvjAg5fnk= +-----END PRIVATE KEY----- +" + +test "Печатаем эталонный ключ gost2001" { + set etalon [openssl [list pkey -text -noout << $key]] +} 0 "Private key: 499F36A98A6EE9142351EA0A979B2FAEF62E32B88778042FB9222F8C08397E79 +Parameter set: id-GostR3410-2001-CryptoPro-A-ParamSet +" + +test "Конвертируем в DER и проверяем, что ключ тот же gost2001" { + openssl [list pkcs8 -outform DER -out pkcs8-1.der -nocrypt << $key] + openssl [list pkey -inform DER -text -noout -in pkcs8-1.der] +} 0 $etalon + + +save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} +test "Зашифровываем незашифрованный ключ gost2001, параметры CryptoPro-A" { + makeFile pkcs8-1A.key $key + set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-A-ParamSet" + set env(GOST_PBE_HMAC) "md_gost94" + openssl [list pkcs8 -v2 gost89 -passout pass:qwertyu -in pkcs8-1A.key -topk8 -out encA.key << $key] + file exists encA.key +} 0 1 +restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} + +test -skip {![file exists encA.key]} "Проверяем OID-ы PBE" { + set res [extract_oids encA.key] + regexp "HMAC GOST 34\.11-94" $res && regexp "GOST .*89" +} 0 1 + +test "Расшифровываем зашифрованный ключ gost2001" { + set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in encA.key]] + openssl [list pkey -text -noout << $unencrypted] + +} 0 $etalon + +save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} +test "Зашифровываем незашифрованный ключ gost2001, параметры CryptoPro-B" { + makeFile pkcs8-1B.key $key + set env(CRYPT_PARAMS) "id-Gost28147-89-CryptoPro-B-ParamSet" + set env(GOST_PBE_HMAC) "md_gost94" + openssl [list pkcs8 -v2 gost89 -passout pass:qwertyu -in pkcs8-1B.key -topk8 -out encB.key << $key] + file exists encB.key +} 0 1 +restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} + +test -skip {![file exists encB.key]} "Проверяем OID-ы PBE" { + set res [extract_oids encB.key] + regexp "HMAC GOST 34\.11-94" $res && regexp "GOST .*89" +} 0 1 + + +test "Расшифровываем зашифрованный ключ gost2001" { + set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in encB.key]] + openssl [list pkey -text -noout << $unencrypted] + +} 0 $etalon + + +test "Расшифровываем ключ, созданный mkkey" { + makeFile pkcs8-2.key "-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAjIvbrnGmGbTAIC +CAAwCgYGKoUDAgIKBQAwHQYGKoUDAgIVMBMECOtWtCMQo3dzBgcqhQMCAh8B +BFZFPKP6qDKi57rmas1U2fUjyZwjmrk6Y+naeWG/BTVJNJklW3HaHP+wuIFb +bxdi6rTNsYqxWm26qUHz6Op5SvCm0y+f8zE9cACQ5KQnFvNlojHvzmjO+Q== +-----END ENCRYPTED PRIVATE KEY----- +" + set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -nocrypt -topk8 -in pkcs8-2.key ]] + openssl [list pkey -text -noout << $unencrypted] +} 0 $etalon + +test "Расшифровываем ключ, созданный mkkey, русский пароль" { + set env(PASS) [encoding convertfrom [encoding convertto utf-8 [rus "йцукенг"]]] + makeFile pkcs8-3.key "-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAgSfbLQ+fNe0AIC +CAAwCgYGKoUDAgIKBQAwHQYGKoUDAgIVMBMECJJ1Qd/rIBxqBgcqhQMCAh8B +BFZWfyFs12456ECvzNyg7LkPNAJS7qPih78kY4DJP7ty4bMydfCkfg20fMNl +O2zlJtg37z9vbhvqdWODCXc/XJ+Txmw3GLVDcvwQ/0woebcPlNUvMd9BzA== +-----END ENCRYPTED PRIVATE KEY----- +" + set unencrypted [openssl [list pkcs8 -passin env:PASS -nocrypt -topk8 -in pkcs8-3.key ]] + grep Private [openssl [list pkey -text -noout << $unencrypted]] +} 0 "Private key: 894150BCD66A400C198154D68E5817A6EF3546983863B57F6D04F5C14FD766CC\n" + +set key256 "-----BEGIN PRIVATE KEY----- +MEgCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEIgIgK/ezK4Z5 +GCo/srftX/HPs2AmcFKffF3/RWokTAKxMcM= +-----END PRIVATE KEY----- +" + +test "Печатаем эталонный ключ gost2012_256" { + set etalon256 [openssl [list pkey -text -noout << $key256]] +} 0 "Private key: 2BF7B32B8679182A3FB2B7ED5FF1CFB3602670529F7C5DFF456A244C02B131C3 +Parameter set: id-GostR3410-2001-CryptoPro-A-ParamSet +" + +test "Конвертируем в DER и проверяем, что ключ тот же gost2012_256" { + openssl [list pkcs8 -outform DER -out pkcs8-256.der -nocrypt << $key] + openssl [list pkey -inform DER -text -noout -in pkcs8-256.der] +} 0 $etalon + +save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} +test "Зашифровываем незашифрованный ключ gost2012_256, параметры TK26 (умолчательные)" { + makeFile pkcs8-256.key $key256 + catch {unset env(CRYPT_PARAMS)} + catch {unset env(GOST_PBE_HMAC)} + openssl [list pkcs8 -v2 gost89 -passout pass:qwertyu -in pkcs8-256.key -topk8 -out enc256.key << $key] + file exists enc256.key +} 0 1 +restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} + +test -skip {![file exists enc256.key]} "Проверяем OID-ы PBE" { + set res [extract_oids enc256.key] + regexp "HMAC GOST 34\.11-2012" $res && regexp "GOST .*89" +} 0 1 + +test "Расшифровываем зашифрованный ключ gost2012_256" { + set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in enc256.key]] + openssl [list pkey -text -noout << $unencrypted] + +} 0 $etalon256 + +set key512 "-----BEGIN PRIVATE KEY----- +MGsCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRDAkEAiCjF +2rwOmb5YwNnyObveusCDO+kw33jBijSrPiye155EO4ABz2aG8SHOTObVv4dFgtfZ +g7wCuOZN3D6RSByFJA== +-----END PRIVATE KEY----- +" + +save_env2 {CRYPT_PARAMS GOST_PBE_HMAC} +test "Печатаем эталонный ключ gost2012_512" { + set etalon512 [openssl [list pkey -text -noout << $key512]] +} 0 "Private key: 8828C5DABC0E99BE58C0D9F239BBDEBAC0833BE930DF78C18A34AB3E2C9ED79E443B8001CF6686F121CE4CE6D5BF874582D7D983BC02B8E64DDC3E91481C8524 +Parameter set: GOST R 34.10-2012 (512 bit) ParamSet A +" + +test "Конвертируем в DER и проверяем, что ключ тот же gost2012_512" { + openssl [list pkcs8 -outform DER -out pkcs8-512.der -nocrypt << $key] + openssl [list pkey -inform DER -text -noout -in pkcs8-512.der] +} 0 $etalon + +test "Зашифровываем незашифрованный ключ gost2012_512, параметры TK26 (умолчательные)" { + makeFile pkcs8-512.key $key512 + catch {unset env(CRYPT_PARAMS)} + set env(GOST_PBE_HMAC) "md_gost12_512" + openssl [list pkcs8 -v2 gost89 -passout pass:qwertyu -in pkcs8-512.key -topk8 -out enc512.key << $key] + file exists enc512.key +} 0 1 +restore_env2 {CRYPT_PARAMS GOST_PBE_HMAC} + +test -skip {![file exists enc512.key]} "Проверяем OID-ы PBE" { + set res [extract_oids enc512.key] + regexp "HMAC GOST 34\.11-2012" $res && regexp "GOST .*89" +} 0 1 + +test "Расшифровываем зашифрованный ключ gost2012 512 bit" { + set unencrypted [openssl [list pkcs8 -passin pass:qwertyu -topk8 -nocrypt -in enc512.key]] + openssl [list pkey -text -noout << $unencrypted] + +} 0 $etalon512 + +end_tests diff --git a/tcl_tests/pkgIndex.tcl b/tcl_tests/pkgIndex.tcl new file mode 100644 index 0000000..f4a3345 --- /dev/null +++ b/tcl_tests/pkgIndex.tcl @@ -0,0 +1,6 @@ +package ifneeded test 0.2 [list source [file join $dir test.tcl]] +package ifneeded testlib 0.1 [list source [file join $dir testlib.tcl]] +package ifneeded fgetopt 0.1 [list source [file join $dir fgetopt.tcl]] +package ifneeded asn 0.7.1 [list source [file join $dir asn.tcl]] +package ifneeded base64 2.3.2 [list source [file join $dir base64.tcl]] +package ifneeded ossltest 0.7 [list source [file join $dir ossltest.tcl]] diff --git a/tcl_tests/plain.enc b/tcl_tests/plain.enc new file mode 100644 index 0000000..2dd9652 --- /dev/null +++ b/tcl_tests/plain.enc @@ -0,0 +1 @@ +Test data to encrypt \ No newline at end of file diff --git a/tcl_tests/private/gost2001_A.pem b/tcl_tests/private/gost2001_A.pem new file mode 100644 index 0000000..73eeef2 --- /dev/null +++ b/tcl_tests/private/gost2001_A.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MH8CAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgQgNsz+6JQm0UYw +I90/psShKQrktCj3ehyKe3G7dVuo+ySgODA2BggqhQMCCQMIATEqBChCIIzvjjnM +EjW957oYXmI1tlNIIEMFy50EOPk4d/akd8k9UEUfJQ31 +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2001_B.pem b/tcl_tests/private/gost2001_B.pem new file mode 100644 index 0000000..cf8dd4c --- /dev/null +++ b/tcl_tests/private/gost2001_B.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MH8CAQAwHAYGKoUDAgITMBIGByqFAwICIwIGByqFAwICHgEEIgQgvVb0e4AmOEIH +jKiZj+29OgbFVwy+XHI48yKm8FJyjQygODA2BggqhQMCCQMIATEqBCh3mHOU3GbN +IZuseCRP836vV2rBLOuQrVznWHPOb9VC0lB6eNOhY1GH +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2001_C.pem b/tcl_tests/private/gost2001_C.pem new file mode 100644 index 0000000..b8dc57c --- /dev/null +++ b/tcl_tests/private/gost2001_C.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MH8CAQAwHAYGKoUDAgITMBIGByqFAwICIwMGByqFAwICHgEEIgQgbIM6LxPe0Ave +N+yRZBsb7jmIyZET+YiWpiN3T4sKGiOgODA2BggqhQMCCQMIATEqBCgftKdkhEXD +VG50lrUkn9EbqriBTKRO8SK2Yu9VJfeVt9uDpYQ9dMu2 +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2001_XA.pem b/tcl_tests/private/gost2001_XA.pem new file mode 100644 index 0000000..8884bd2 --- /dev/null +++ b/tcl_tests/private/gost2001_XA.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MH8CAQAwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgEEIgQgSGtJ/s0X+5ld +2wilev1VMOVYAiYPqxJeBCovJlnsB3agODA2BggqhQMCCQMIATEqBChquxeySqNx +XJ+zRfoydNB3ZnI7WHvyCdevrpnUXi43QfJ7Bofldn7G +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2001_XB.pem b/tcl_tests/private/gost2001_XB.pem new file mode 100644 index 0000000..a94b9aa --- /dev/null +++ b/tcl_tests/private/gost2001_XB.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MH8CAQAwHAYGKoUDAgITMBIGByqFAwICJAEGByqFAwICHgEEIgQgYAQQC17Sjt3F +rg9m7PyzD9Pa2eAYTMr8ltX3IBOmukWgODA2BggqhQMCCQMIATEqBCgl8zuE43h4 +78dfpVXoV7bD85G2SIY1/d7o+bAr8AiQBumvzlnQr2ka +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_256_A.pem b/tcl_tests/private/gost2012_256_A.pem new file mode 100644 index 0000000..5f13bb8 --- /dev/null +++ b/tcl_tests/private/gost2012_256_A.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGCAgEAMB8GCCqFAwcBAQEBMBMGByqFAwICIwEGCCqFAwcBAQICBCIEIOTjkLas +e0/SvSH/hFRSfBopnkP9a6mqCqP/zEf6WwFsoDgwNgYIKoUDAgkDCAExKgQoXql/ +cWe/7Tgh3csptrfiEgAaWgGS+VhzbJnZLvn9/STU1/46KWA+0A== +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_256_B.pem b/tcl_tests/private/gost2012_256_B.pem new file mode 100644 index 0000000..2e7c6d2 --- /dev/null +++ b/tcl_tests/private/gost2012_256_B.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGCAgEAMB8GCCqFAwcBAQEBMBMGByqFAwICIwIGCCqFAwcBAQICBCIEIO6zQc6w +YqWlSTp4BBlVa3q+6w96tRtpWlp+LZjb5ShyoDgwNgYIKoUDAgkDCAExKgQotOvO +EGwCt0ZcyZA+Lq3RwctxfJMIImyY48X/UilpdLTbfuxNTrAP7Q== +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_256_C.pem b/tcl_tests/private/gost2012_256_C.pem new file mode 100644 index 0000000..7ed1a14 --- /dev/null +++ b/tcl_tests/private/gost2012_256_C.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGCAgEAMB8GCCqFAwcBAQEBMBMGByqFAwICIwMGCCqFAwcBAQICBCIEIAmU+PyI +r/n63dlQoJhZu0NNJgay2KEFmtb2JvDsLDtzoDgwNgYIKoUDAgkDCAExKgQoTmXk +7VWKAP0WNGF3y26CldydpwA1llejlnI3eZ0IaIq6In4K3SzWjw== +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_256_XA.pem b/tcl_tests/private/gost2012_256_XA.pem new file mode 100644 index 0000000..e32ce8a --- /dev/null +++ b/tcl_tests/private/gost2012_256_XA.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGCAgEAMB8GCCqFAwcBAQEBMBMGByqFAwICJAAGCCqFAwcBAQICBCIEIAlnokn7 +Vw8a7IykYa4EYlv6zdit8QG5Fxs9wdWF/LuyoDgwNgYIKoUDAgkDCAExKgQopAxM +Ymu/ufjc2DAxKwaMaeI1cQbNZGApKGrBgFr5/azc+wYpkV/tIg== +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_256_XB.pem b/tcl_tests/private/gost2012_256_XB.pem new file mode 100644 index 0000000..aa2d421 --- /dev/null +++ b/tcl_tests/private/gost2012_256_XB.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGCAgEAMB8GCCqFAwcBAQEBMBMGByqFAwICJAEGCCqFAwcBAQICBCIEIB7ziMjS +yZxVTj9kzt5nyHoz0BVpvWkb8dAxo5SjV9KBoDgwNgYIKoUDAgkDCAExKgQoM1Rx +tOEXmeWQll3cuet7UDp0YEmVid1AMvvdx3MD4LVthAmrIYKUCg== +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_512_A.pem b/tcl_tests/private/gost2012_512_A.pem new file mode 100644 index 0000000..fe04189 --- /dev/null +++ b/tcl_tests/private/gost2012_512_A.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGkAgEAMCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAQYIKoUDBwEBAgMEQgRAdBFk +9bJa60Rw9jvTNdtfZ3ImZuep2Klx5Fpap1bpBE4u/T4CI/R2nyjuWCBKPmbBHJO7 +4Z/BTUYYe04l0gWWZaA4MDYGCCqFAwIJAwgBMSoEKCHK4j/oPjNmh1w4eH6Zx1cQ +kzLi1QCYb8u45IFtpBx0jF4O0etcYKo= +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/gost2012_512_B.pem b/tcl_tests/private/gost2012_512_B.pem new file mode 100644 index 0000000..e59abff --- /dev/null +++ b/tcl_tests/private/gost2012_512_B.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIGkAgEAMCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAgYIKoUDBwEBAgMEQgRAQ8Hd +iGVmLyWQFF7lR2VnBIV/npcxJOTrsdhnSpNczZVT+ZWEvY66IQMsUPCVUcgZIYyp +44E3v8kD6x0EuCmPcqA4MDYGCCqFAwIJAwgBMSoEKG0+06BM69y0EvoCeE4fP8jB +XPhDcF9daMRu299ZaXv2H9o/OO+nC5A= +-----END PRIVATE KEY----- diff --git a/tcl_tests/private/rsa_1024.pem b/tcl_tests/private/rsa_1024.pem new file mode 100644 index 0000000..2db01ec --- /dev/null +++ b/tcl_tests/private/rsa_1024.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANiP17PGKutiabuB +HCtGk5sBn/4Sv/X7d2HQBDwpxKx0t8U7Z+hRtzLLCRCyNiY+vnbGHkQKfAi/HfaA +awvG9mGBBOA5jdRU0c9aJUNEHH4I/EkYzEkWxZHTNTVbZM4CUHJvA7AM8kRXyI00 +MTKirF0/clTXaiAryhprioSw0ulxAgMBAAECgYEAr5ch3D/neOdYt5Gd13DoKZyN +ryJgxv/X8lUJugZb00Dn6GGchIANPH/nn8P/p87j+8XzsFOX8jeUAdRp9yihhzuN +doY8EW0UEKXApHh5Y0UmlC1YqFrgtk8Y6k4bK6GewNXPekMjTLUhR+cECG6I8yAt +Jtlwo+539uBld6pXEAECQQDuH6IrLZHXfxTYOeDnvYnncKrfk7szUTRV/2q8GAlw +1bqQPB1xuautWrgw90HRLAvD27KOFgZ0R9fNfULaEA3xAkEA6NHTsZNgZs7VF5Z8 +krR6SkszIyR3UyurvPrusR44lT6ypSsulFV+SrjFOAwbuLjl+fap9oGLI1tsbFUA +UeMTgQJAKrX71XjP/vIpX/tJruddU+jujTmnSzYWiBJPJ7u7/cQoOXS+50YhV++8 +t/Oxl34qAhBm/3tN3w9/0rjUA977UQJAWLHvKvxRQnlTVvwekyksWXdSkPXIe6fs +cj9KhbFUrw7GsgPEaAA177N6dsKuIO5XtqWQ1Hc/kYW3xYGQcKHIgQJBAIaEvBhO +PkoT0apaZgriQnuWpqGCnFYTjAY7zSr+Y5kI1lnb3DPipIsZZ5X7VQ/c+64nR2M/ +X1NAW+G8zHBxK6o= +-----END PRIVATE KEY----- diff --git a/tcl_tests/req-genpkey.try b/tcl_tests/req-genpkey.try new file mode 100644 index 0000000..e333dc0 --- /dev/null +++ b/tcl_tests/req-genpkey.try @@ -0,0 +1,83 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +# создание секретного ключа +# создание заявки и самоподписанного сертификата командой req +# проверка OIDов алгоритма во всех структурах +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Создание ключей и заявок, команда genpkey" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2001:XA gost2001:XB gost2012_256:0 gost2012_256:A gost2012_256:B gost2012_256:C gost2012_256:XA gost2012_256:XB gost2012_512:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username pkey_$alg_fn + foreach {alg_only params} [split $alg :] break + +test -createsfiles $username/seckey.pem "Секретный ключ, алгоритм $alg" { + file delete -force $username + file mkdir $username + openssl "genpkey -algorithm $alg_only -pkeyopt paramset:$params -out $username/seckey.pem" + expr {[file size $username/seckey.pem] > 0} +} 0 1 + +test -skip {![file exists $username/seckey.pem]} "OID в секретном ключе" { + extract_oids $username/seckey.pem +} 0 [mkObjList [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]]] + +test -skip {![file exists $username/seckey.pem]} "Алгоритм $alg, заявка по секретному ключу" { + makeFile $username/req.conf [makeConf] + openssl "req -new -config $username/req.conf -key $username/seckey.pem -out $username/req.pem" + expr {[file size $username/req.pem] > 0} +} 0 1 + +test -skip {![file exists $username/req.pem]} "Подпись под заявкой корректна" { + grep "verif" [openssl "req -verify -in $username/req.pem"] +} 0 {verify OK +} + +test -skip {![file exists $username/req.pem]} "OID в заявке, алгоритм $alg" { + extract_oids $username/req.pem +} 0 [mkObjList [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [hash_with_sign_long_name $alg]] + +test -skip {![file exists $username/seckey.pem]} "Алгоритм $alg, сертификат по секретному ключу" { + openssl "req -new -x509 -config $username/req.conf -key $username/seckey.pem -out $username/cert.pem" + expr {[file size $username/cert.pem] > 0} +} 0 1 + +test -skip {![file exists $username/cert.pem]} "OID в сертификате" { + extract_oids $username/cert.pem +} 0 [mkObjList [hash_with_sign_long_name $alg] [alg_long_name $alg] [pubkey_long_name $alg]\ + [param_hash_long_name [param_hash $alg]] [hash_with_sign_long_name $alg]] + +test -createsfiles "$username/seckey.der" "Алгоритм $alg сохраняем ключ в DER-формате" { + openssl "genpkey -algorithm $alg_only -pkeyopt paramset:$params -out $username/seckey.der -outform DER" + file exists $username/seckey.der +} 0 1 + +test -skip {![file exists $username/seckey.der]} "OID в секретном ключе $alg DER" { + extract_oids $username/seckey.der DER +} 0 [mkObjList [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]]] + +test -skip {![file exists $username/seckey.der]} -createsfiles $username/req2.pem "Создаем заявку из ключа в формате DER" { + openssl "req -new -config $username/req.conf -key $username/seckey.der -keyform der -out $username/req2.pem" + expr {[file size $username/req2.pem] > 0} +} 0 1 + +test -skip {![file exists $username/req2.pem]} "Подпись под заявкой корректна" { + grep "verif" [openssl "req -verify -in $username/req2.pem"] +} 0 {verify OK +} + +} + + +end_tests diff --git a/tcl_tests/req-newkey.try b/tcl_tests/req-newkey.try new file mode 100644 index 0000000..fe05fa6 --- /dev/null +++ b/tcl_tests/req-newkey.try @@ -0,0 +1,95 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +if {[info exists env(PKG_PATH)]} { + lappend auto_path $env(PKG_PATH) +} else { + lappend auto_path [file dirname [info script]] +} +package require ossltest +package require asn 0.4.1 +cd $::test::dir + +switch -exact [engine_name] { + "ccore" { + set no_param_set "no public key parameters set" + set invalid_paramset "invalid pubic key paramset name" + } + "open" { + set no_param_set "no parameters set" + set invalid_paramset "parameter error" + } +} + +start_tests "Создание ключей и заявок, команда req -newkey" +makeCA +foreach {alg descr code result} { +gost2001: "ГОСТ 2001 Криптопро" 1 no_param_set +gost2001:A "ГОСТ 2001 Криптопро A" 0 1.2.643.2.2.35.1 +gost2001:B "ГОСТ 2001 Криптопро B" 0 1.2.643.2.2.35.2 +gost2001:C "ГОСТ 2001 Криптопро C" 0 1.2.643.2.2.35.3 +gost2001:D "ГОСТ 2001 Криптопро неверный параметр" 1 invalid_paramset +gost2001:test "ГОСТ 2001 Криптопро тестовый" 0 1.2.643.2.2.35.0 +gost2001:XA "ГОСТ 2001 Криптопро XA" 0 1.2.643.2.2.36.0 +gost2001:XB "ГОСТ 2001 Криптопро XB" 0 1.2.643.2.2.36.1 +gost2001:id-GostR3410-2001-CryptoPro-XchB-ParamSet "ГОСТ 2001 Криптопро XB по имени" 0 1.2.643.2.2.36.1 +gost2001:1.2.643.2.2.36.1 "ГОСТ 2001 Криптопро XB по OID" 0 1.2.643.2.2.36.1 +gost2001:1.2.840.113549.1.1.1 "Недопустимый OID" 1 invalid_paramset +gost2001:RSAencryption: "Недопустимое имя объекта" 1 invalid_paramset +gost2012_256: "ГОСТ 2012 256 бит" 1 no_param_set +gost2012_256:A "ГОСТ 2012 256 бит Криптопро A" 0 1.2.643.2.2.35.1 +gost2012_256:B "ГОСТ 2012 256 бит Криптопро B" 0 1.2.643.2.2.35.2 +gost2012_256:C "ГОСТ 2012 256 бит Криптопро C" 0 1.2.643.2.2.35.3 +gost2012_256:D "ГОСТ 2012 256 бит Криптопро неверный параметр" 1 invalid_paramset +gost2012_256:id-GostR3410-2001-CryptoPro-B-ParamSet "ГОСТ 2012 256 бит Криптопро B по имени" 0 1.2.643.2.2.35.2 +gost2012_256:1.2.643.2.2.35.1 "ГОСТ 2012 256 бит Криптопро A по OID" 0 1.2.643.2.2.35.1 +gost2012_256:1.2.840.113549.1.1.1 "Недопустимый OID" 1 invalid_paramset +gost2012_256:RSAencryption: "Недопустимое имя объекта" 1 invalid_paramset +gost2012_512: "ГОСТ 2012 512 бит" 1 no_param_set +gost2012_512:A "ГОСТ 2012 512 бит ТК26 A" 0 1.2.643.7.1.2.1.2.1 +gost2012_512:B "ГОСТ 2012 512 бит ТК26 B" 0 1.2.643.7.1.2.1.2.2 +gost2012_512:C "ГОСТ 2012 512 бит неверный параметр" 1 invalid_paramset +gost2012_512:id-tc26-gost-3410-2012-512-paramSetB "ГОСТ 2012 512 бит набор параметров B по имени" 0 1.2.643.7.1.2.1.2.2 +gost2012_512:1.2.643.7.1.2.1.2.1 "ГОСТ 2012 512 бит набор параметров A по OID" 0 1.2.643.7.1.2.1.2.1 +gost2012_512:1.2.643.2.2.35.1 "Недопустимый OID" 1 invalid_paramset +gost2012_512:RSAencryption: "Недопустимое имя объекта" 1 invalid_paramset + + +} { + + switch -exact $result { + "no_param_set" { set result $no_param_set } + "invalid_paramset" { set result $invalid_paramset } + } + + set index [string first ":" $alg] + if {$index != -1} { + set algname [string range $alg 0 [expr $index-1]] + set algparam [string range $alg [expr $index+1] end] + } else { + set algname $alg + set algparam "" + } + if {$algparam ne ""} { + set algparamcmdline "-pkeyopt paramset:$algparam" + } else { + set algparamcmdline "" + } + + test -skip {{[engine_name] eq "open" && $alg eq "gost2001:test"}} $descr { + openssl "req -newkey $algname $algparamcmdline -keyout test.key -out test.req -batch -nodes -config $::test::ca/req.conf" + set pkcs8 [readpem test.key] + asn::asnGetSequence pkcs8 seq1 + asn::asnGetInteger seq1 int0 + asn::asnGetSequence seq1 seq2 + asn::asnGetObjectIdentifier seq2 oid + asn::asnGetSequence seq2 seq3 + asn::asnGetObjectIdentifier seq3 oid + join $oid . + } $code $result + log "errorInfo: $errorInfo" + + test -skip {![file exists test.req]} "Заявка подписана корректно" { + grep "verif" [openssl "req -verify -in test.req"] + } 0 "verify OK\n" +} +end_tests diff --git a/tcl_tests/runtest.bat b/tcl_tests/runtest.bat new file mode 100755 index 0000000..68da053 --- /dev/null +++ b/tcl_tests/runtest.bat @@ -0,0 +1,88 @@ +@echo off + +rem Тесты на коммерческий энжин выполняются с тем ДСЧ, на использование +rem которого сконфигурирован КриптоПакет (то есть который указан в файле +rem конфигурации или переменной окружения RNG). Исключение - наборы тестов +rem prng.try, rng.try, rng-seed.try (всегда тестируют ДСЧ PROGRAM) и +rem rng2.try (тестирует все ДСЧ, которые найдет на компьютере). + + +rem Состав набора тестов +rem 1. Этот скрипт +rem 2. Файлы *.try +rem 3. Файлы *.tcl +rem 4. Файлы *.ciphers +rem 5. calcstat +rem 6. oidfile +rem 7. name2oid.tst + +rem Пререквизиты, которые должны быть установлены на машине: +rem 1. tclsh. +rem 2. ssh (что характерно, называться должен именно так и не должен выводить +rem лишних сообщений), мы используем ssh.bat вокруг putty: +rem @plink -l build %* +rem Должен и настроен заход по ключам без пароля на lynx и все используемые +rem эталонники. Ключи этих машин должны быть в knownhosts с полными доменными +rem именами серверов, то есть lynx.lan.cryptocom.ru и т.д. (для putty +rem knownhosts хранятся в реесте). +rem В Firewall Windows необходимо прописать исключение, разрешающее +rem соединения для программы openssl.exe. Внимание, Windows неправильно +rem трактует понятие "локальная сеть" в описании исключения, нужно либо +rem выставлять "любой компьютер", либо явно задавать маску 10.51.0.0/255.255.0.0 + + +IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe +IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe + +%TCLSH% getengine.tcl > engine_name.txt +set /p ENGINE_NAME= < engine_name.txt +del engine_name.txt + +hostname > host_name.txt +set /p HOST_NAME= < host_name.txt +del host_name.txt +set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME% +rmdir /s /q %TESTDIR% +mkdir %TESTDIR% +copy oidfile %TESTDIR% +set OTHER_VERSION=../OtherVersion + +IF %ENGINE_NAME%==cryptocom ( + set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io + set OTHER_DIR=../%HOST_NAME%-bat-gost +) ELSE ( + IF %ENGINE_NAME%==gost ( + set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io + set OTHER_DIR=../%HOST_NAME%-bat-cryptocom + ) ELSE ( + echo No GOST providing engine found + exit 1 + ) +) + +set PKCS7_COMPATIBILITY_TESTS=smime_cs cmsenc_cs cmsenc_sc +set CLIENT_TESTS=cp20 cp21 +set WINCLIENT_TESTS=p1-1xa-tls1-v-cp36r4-srv p1-1xa-tls1-v-cp39-srv p1-1xa-tls1-v-cp4-01 p2-1xa-tls1-v-cp4-01 p2-2xa-tls1-v-cp4-12S p2-5xa-tls1-v-cp4-12L p1-1xa-tls1-v-cp4r3-01 p2-1xa-tls1-v-cp4r3-01 p2-2xa-tls1-v-cp4r3-01 p2-5xa-tls1-v-cp4r3-01 p1-1xa-tls1_1-v-cp4r3-01 p2-1xa-tls1_1-v-cp4r3-01 p2-2xa-tls1_1-v-cp4r3-01 p2-5xa-tls1_1-v-cp4r3-01 p1-1xa-tls1_2-v-cp4r3-01 p2-1xa-tls1_2-v-cp4r3-01 p2-2xa-tls1_2-v-cp4r3-01 p2-5xa-tls1_2-v-cp4r3-01 p1-1xa-tls1-v-cp5-01 p2-1xa-tls1-v-cp5-01 p2-2xa-tls1-v-cp5-01 p2-5xa-tls1-v-cp5-01 p1-1xa-tls1_1-v-cp5-01 p2-1xa-tls1_1-v-cp5-01 p2-2xa-tls1_1-v-cp5-01 p2-5xa-tls1_1-v-cp5-01 p1-1xa-tls1_2-v-cp5-01 p2-1xa-tls1_2-v-cp5-01 p2-2xa-tls1_2-v-cp5-01 p2-5xa-tls1_2-v-cp5-01 +set SERVER_TESTS=cp20 cp21 csp36r4 csp39 csp4 csp4r3 csp5 +set OPENSSL_DEBUG_MEMORY=on + +FOR %%t IN (%BASE_TESTS%) DO %TCLSH% %%t.try +FOR %%t IN (%PKCS7_COMPATIBILITY_TESTS%) DO %TCLSH% %%t.try +FOR %%t IN (%SERVER_TESTS%) DO %TCLSH% server.try %%t +FOR %%t IN (%CLIENT_TESTS%) DO %TCLSH% client.try %%t +set CVS_RSH=ssh +FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t +IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try +IF EXIST %TESTDIR%\%OTHER_VERSION% ( + set OTHER_DIR=%OTHER_VERSION% + IF %ENGINE_NAME%==cryptocom ( + set ALG_LIST="gost2001:A gost2001:B gost2001:C" + set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" + ) ELSE ( + set ALG_LIST="gost2001:A gost2001:B gost2001:C" + set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" + ) + %TCLSH% interop.try +) + +%TCLSH% calcstat %TESTDIR%\stats %TESTDIR%\test.result diff --git a/tcl_tests/runtest.sh b/tcl_tests/runtest.sh new file mode 100644 index 0000000..a59c946 --- /dev/null +++ b/tcl_tests/runtest.sh @@ -0,0 +1,155 @@ +#!/bin/sh +# -*- coding: cp1251 -*- + +# Состав набора тестов +# 1. Этот скрипт +# 2. Файлы *.try +# 3. Файлы *.tcl +# 4. Файлы *.ciphers +# 5. calcstat +# 6. oidfile +# 7. name2oid.tst + +# Пререквизиты, которые должны быть установлены на машине: +# 1. tclsh. Может называться с версией (см. список версий ниже в цикле +# перебора) +# 2. ssh (что характерно, называться должен именно так). Должен быть +# настроен заход по ключам без пароля на lynx и все используемые эталонники. +# Ключи этих машин должны быть в knownhosts (с полными доменными именами +# серверов, то есть lynx.lan.cryptocom.ru и т.д.) +# 3. Под Windows скрипт выполняется в среде MinGW, при этом нужно "донастроить" +# ssh, а именно создать в разделе реестра +# HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters +# DWORD-ключ DisableUserTOSSetting=0 + +CRYPTOPACK_MAIN_VERSION=3 + +if [ -z "$OPENSSL_APP" ]; then + if [ "$OS" != "Windows NT" -a "$OS" != "Windows_NT" ]; then + if [ -x /opt/cryptopack$CRYPTOPACK_MAIN_VERSION/bin/openssl ]; then + OPENSSL_APP=/opt/cryptopack$CRYPTOPACK_MAIN_VERSION/bin/openssl + elif [ -x /usr/local/cryptopack$CRYPTOPACK_MAIN_VERSION/bin/openssl ];then + OPENSSL_APP=/usr/local/cryptopack$CRYPTOPACK_MAIN_VERSION/bin/openssl + fi + else + if [ -x c:/cryptopack$CRYPTOPACK_MAIN_VERSION/bin/openssl.exe ] ; then + OPENSSL_APP=c:/cryptopack$CRYPTOPACK_MAIN_VERSION/bin/openssl.exe + fi + fi +fi +if [ -z "$OPENSSL_APP" ]; then + echo "openssl not found" + exit 1 +else + echo "Using $OPENSSL_APP as openssl" +fi + + +if [ -z "$TCLSH" ]; then + for version in "" 8.4 84 8.5 85 8.6 86; do + for command in tclsh$version; do + for dir in `echo "/opt/cryptopack/bin:$PATH" | sed -e 's/:/ /g'`; do + echo "Checking $dir/$command" + if [ -x $dir/$command ]; then + TCLSH=$dir/$command + break 3 + fi + done + done + done +fi + +if [ -z "$TCLSH" ]; then + echo "tclsh not found in PATH, neither with nor without version, exiting" + exit 2 +else + echo "Using $TCLSH as tclsh" +fi + +APP_SUFFIX=`basename $OPENSSL_APP .exe|sed s/openssl//` +[ -n "$OPENSSL_APP" ]&& export OPENSSL_APP +ENGINE_NAME=`$TCLSH getengine.tcl` +export ENGINE_NAME +TESTDIR=`hostname`-$ENGINE_NAME +[ -n "$APP_SUFFIX" ] && TESTDIR=${TESTDIR}-${APP_SUFFIX} +[ -d ${TESTDIR} ] && rm -rf ${TESTDIR} +mkdir ${TESTDIR} +cp oidfile ${TESTDIR} +export TESTDIR + +case "$ENGINE_NAME" in + gostkc3) + BASE_TEST="1" + ;; + cryptocom) + BASE_TESTS="engine dgst mac pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io" + OTHER_DIR=`echo $TESTDIR |sed 's/cryptocom/gost/'` + ;; + gost) + OTHER_DIR=`echo $TESTDIR |sed 's/gost/cryptocom/'` + ;; + *) + echo "No GOST=providing engine found" 1>&2 + exit 1; +esac +if [ -x copy_param ]; then + BASE_TESTS="$BASE_TESTS apache" +fi +PKCS7_COMPATIBILITY_TESTS="smime_cs cmsenc_cs cmsenc_sc" +SERVER_TESTS="cp20 cp21 csp36r4 csp39 csp4 csp4r3 csp5" +CLIENT_TESTS="cp20 cp21" +WINCLIENT_TESTS="p1-1xa-tls1-v-cp36r4-srv p1-1xa-tls1-v-cp39-srv p1-1xa-tls1-v-cp4-01 p2-1xa-tls1-v-cp4-01 p2-2xa-tls1-v-cp4-12S p2-5xa-tls1-v-cp4-12L p1-1xa-tls1-v-cp4r3-01 p2-1xa-tls1-v-cp4r3-01 p2-2xa-tls1-v-cp4r3-01 p2-5xa-tls1-v-cp4r3-01 p1-1xa-tls1_1-v-cp4r3-01 p2-1xa-tls1_1-v-cp4r3-01 p2-2xa-tls1_1-v-cp4r3-01 p2-5xa-tls1_1-v-cp4r3-01 p1-1xa-tls1_2-v-cp4r3-01 p2-1xa-tls1_2-v-cp4r3-01 p2-2xa-tls1_2-v-cp4r3-01 p2-5xa-tls1_2-v-cp4r3-01 p1-1xa-tls1-v-cp5-01 p2-1xa-tls1-v-cp5-01 p2-2xa-tls1-v-cp5-01 p2-5xa-tls1-v-cp5-01 p1-1xa-tls1_1-v-cp5-01 p2-1xa-tls1_1-v-cp5-01 p2-2xa-tls1_1-v-cp5-01 p2-5xa-tls1_1-v-cp5-01 p1-1xa-tls1_2-v-cp5-01 p2-1xa-tls1_2-v-cp5-01 p2-2xa-tls1_2-v-cp5-01 p2-5xa-tls1_2-v-cp5-01 p8k-5xa-tls1_2-v-cp5-01 p8k-2xa-tls1_2-v-cp5-01 p8m-5xa-tls1_2-v-cp5-01 p8m-2xa-tls1_2-v-cp5-01" +OPENSSL_DEBUG_MEMORY=on +export OPENSSL_DEBUG_MEMORY + +fail=0 +for t in $BASE_TESTS; do + $TCLSH $t.try || fail=1 +done + +ALG_LIST="rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A" OPENSSL_CONF=`pwd`/openssl-gost.cnf $TCLSH ssl.try -clientconf `pwd`/openssl-cryptocom.cnf || fail=1 +ALG_LIST="rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A" OPENSSL_CONF=`pwd`/openssl-gost.cnf $TCLSH ssl.try -serverconf `pwd`/openssl-cryptocom.cnf || fail=1 + +for t in $PKCS7_COMPATIBILITY_TESTS; do + $TCLSH $t.try || fail=1 +done +for t in $SERVER_TESTS; do + $TCLSH server.try $t || fail=1 +done +for t in $CLIENT_TESTS; do + $TCLSH client.try $t || fail=1 +done +if [ -n "WINCLIENT_TESTS" ]; then + if [ -z "$CVS_RSH" ]; then + CVS_RSH=ssh + export CVS_RSH + fi + for t in $WINCLIENT_TESTS; do + $TCLSH wcli.try $t || fail=1 + done +fi +if [ -d $OTHER_DIR ]; then + OTHER_DIR=../${OTHER_DIR} $TCLSH interop.try +fi +if [ -d OtherVersion ] ; then + case "$ENGINE_NAME" in + gostkc3) + ;; + cryptocom) + OTHER_DIR=../OtherVersion ALG_LIST="gost2001:A gost2001:B gost2001:C" ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" $TCLSH interop.try + ;; + gost) + OTHER_DIR=../OtherVersion ALG_LIST="gost2001:A gost2001:B gost2001:C" ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" $TCLSH interop.try + ;; + *) + echo "No GOST=providing engine found" 1>&2 + exit 1; + esac +fi +$TCLSH calcstat ${TESTDIR}/stats ${TESTDIR}/test.result +grep "leaked" ${TESTDIR}/*.log +if [ $fail -ne 0 ]; then + echo "Some tests FAILED." +fi + +exit $fail diff --git a/tcl_tests/runtest1.bat b/tcl_tests/runtest1.bat new file mode 100755 index 0000000..8986104 --- /dev/null +++ b/tcl_tests/runtest1.bat @@ -0,0 +1,81 @@ +@echo off + +rem Состав набора тестов +rem 1. Этот скрипт +rem 2. Файлы *.try +rem 3. Файлы *.tcl +rem 4. Файлы *.ciphers +rem 5. calcstat +rem 6. oidfile +rem 7. name2oid.tst + +rem Пререквизиты, которые должны быть установлены на машине: +rem 1. tclsh. +rem 2. ssh (что характерно, называться должен именно так и не должен выводить +rem лишних сообщений), мы используем ssh.bat вокруг putty: +rem @plink -l build %* +rem Должен и настроен заход по ключам без пароля на lynx и все используемые +rem эталонники. Ключи этих машин должны быть в knownhosts с полными доменными +rem именами серверов, то есть lynx.lan.cryptocom.ru и т.д. (для putty +rem knownhosts хранятся в реесте). +rem В Firewall Windows необходимо прописать исключение, разрешающее +rem соединения для программы openssl.exe. Внимание, Windows неправильно +rem трактует понятие "локальная сеть" в описании исключения, нужно либо +rem выставлять "любой компьютер", либо явно задавать маску 10.51.0.0/255.255.0.0 + + +IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe +IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe + +%TCLSH% getengine.tcl > engine_name.txt +set /p ENGINE_NAME= < engine_name.txt +del engine_name.txt + +hostname > host_name.txt +set /p HOST_NAME= < host_name.txt +del host_name.txt +set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME% +rmdir /s /q %TESTDIR% +mkdir %TESTDIR% +copy oidfile %TESTDIR% +set OTHER_VERSION=../OtherVersion + +IF %ENGINE_NAME%==cryptocom ( + set BASE_TESTS=engine ssl dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts smime_io cms_io smimeenc_io cmsenc_io + set OTHER_DIR=../%HOST_NAME%-bat-gost +) ELSE ( + IF %ENGINE_NAME%==gost ( + set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io + set OTHER_DIR=../%HOST_NAME%-bat-cryptocom + ) ELSE ( + echo No GOST providing engine found + exit 1 + ) +) + +set PKCS7_COMPATIBILITY_TESTS=smime_cs cmsenc_cs cmsenc_sc +set CLIENT_TESTS=cp20 cp21 +set WINCLIENT_TESTS=p1-1xa-tls1-v-cp36r4-srv p1-1xa-tls1-v-cp39-srv p1-1xa-tls1-v-cp4-01 p2-1xa-tls1-v-cp4-01 p2-2xa-tls1-v-cp4-12S p2-5xa-tls1-v-cp4-12L p1-1xa-tls1-v-cp4r3-01 p2-1xa-tls1-v-cp4r3-01 p2-2xa-tls1-v-cp4r3-01 p2-5xa-tls1-v-cp4r3-01 p1-1xa-tls1_1-v-cp4r3-01 p2-1xa-tls1_1-v-cp4r3-01 p2-2xa-tls1_1-v-cp4r3-01 p2-5xa-tls1_1-v-cp4r3-01 p1-1xa-tls1_2-v-cp4r3-01 p2-1xa-tls1_2-v-cp4r3-01 p2-2xa-tls1_2-v-cp4r3-01 p2-5xa-tls1_2-v-cp4r3-01 p1-1xa-tls1-v-cp5-01 p2-1xa-tls1-v-cp5-01 p2-2xa-tls1-v-cp5-01 p2-5xa-tls1-v-cp5-01 p1-1xa-tls1_1-v-cp5-01 p2-1xa-tls1_1-v-cp5-01 p2-2xa-tls1_1-v-cp5-01 p2-5xa-tls1_1-v-cp5-01 p1-1xa-tls1_2-v-cp5-01 p2-1xa-tls1_2-v-cp5-01 p2-2xa-tls1_2-v-cp5-01 p2-5xa-tls1_2-v-cp5-01 +set SERVER_TESTS=cp20 cp21 csp36r4 csp39 csp4 csp4r3 csp5 +set OPENSSL_DEBUG_MEMORY=on + +FOR %%t IN (%BASE_TESTS%) DO %TCLSH% %%t.try +FOR %%t IN (%PKCS7_COMPATIBILITY_TESTS%) DO %TCLSH% %%t.try +rem FOR %%t IN (%SERVER_TESTS%) DO %TCLSH% server.try %%t +rem FOR %%t IN (%CLIENT_TESTS%) DO %TCLSH% client.try %%t +set CVS_RSH=ssh +rem FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t +IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try +IF EXIST %TESTDIR%\%OTHER_VERSION% ( + set OTHER_DIR=%OTHER_VERSION% + IF %ENGINE_NAME%==cryptocom ( + set ALG_LIST="gost2001:A gost2001:B gost2001:C" + set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" + ) ELSE ( + set ALG_LIST="gost2001:A gost2001:B gost2001:C" + set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" + ) + %TCLSH% interop.try +) + +%TCLSH% calcstat %TESTDIR%\stats %TESTDIR%\test.result diff --git a/tcl_tests/runtest2.bat b/tcl_tests/runtest2.bat new file mode 100755 index 0000000..ab7ccaf --- /dev/null +++ b/tcl_tests/runtest2.bat @@ -0,0 +1,81 @@ +@echo off + +rem Состав набора тестов +rem 1. Этот скрипт +rem 2. Файлы *.try +rem 3. Файлы *.tcl +rem 4. Файлы *.ciphers +rem 5. calcstat +rem 6. oidfile +rem 7. name2oid.tst + +rem Пререквизиты, которые должны быть установлены на машине: +rem 1. tclsh. +rem 2. ssh (что характерно, называться должен именно так и не должен выводить +rem лишних сообщений), мы используем ssh.bat вокруг putty: +rem @plink -l build %* +rem Должен и настроен заход по ключам без пароля на lynx и все используемые +rem эталонники. Ключи этих машин должны быть в knownhosts с полными доменными +rem именами серверов, то есть lynx.lan.cryptocom.ru и т.д. (для putty +rem knownhosts хранятся в реесте). +rem В Firewall Windows необходимо прописать исключение, разрешающее +rem соединения для программы openssl.exe. Внимание, Windows неправильно +rem трактует понятие "локальная сеть" в описании исключения, нужно либо +rem выставлять "любой компьютер", либо явно задавать маску 10.51.0.0/255.255.0.0 + + +IF "%OPENSSL_APP%"=="" set OPENSSL_APP=c:\cryptopack3\bin\openssl.exe +IF "%TCLSH%"=="" set TCLSH=c:\Tcl\bin\tclsh.exe + +%TCLSH% getengine.tcl > engine_name.txt +set /p ENGINE_NAME= < engine_name.txt +del engine_name.txt + +hostname > host_name.txt +set /p HOST_NAME= < host_name.txt +del host_name.txt +set TESTDIR=%HOST_NAME%-bat-%ENGINE_NAME% +rem emdir /s /q %TESTDIR% +rem mkdir %TESTDIR% +rem copy oidfile %TESTDIR% +set OTHER_VERSION=../OtherVersion + +IF %ENGINE_NAME%==cryptocom ( + set BASE_TESTS=engine ssl dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts smime_io cms_io smimeenc_io cmsenc_io + set OTHER_DIR=../%HOST_NAME%-bat-gost +) ELSE ( + IF %ENGINE_NAME%==gost ( + set BASE_TESTS=engine dgst pkcs8 enc req-genpkey req-newkey ca smime smime2 smimeenc cms cms2 cmsenc pkcs12 nopath ocsp ts ssl smime_io cms_io smimeenc_io cmsenc_io + set OTHER_DIR=../%HOST_NAME%-bat-cryptocom + ) ELSE ( + echo No GOST providing engine found + exit 1 + ) +) + +set PKCS7_COMPATIBILITY_TESTS=smime_cs cmsenc_cs cmsenc_sc +set CLIENT_TESTS=cp20 cp21 +set WINCLIENT_TESTS=p1-1xa-tls1-v-cp36r4-srv p1-1xa-tls1-v-cp39-srv p1-1xa-tls1-v-cp4-01 p2-1xa-tls1-v-cp4-01 p2-2xa-tls1-v-cp4-12S p2-5xa-tls1-v-cp4-12L p1-1xa-tls1-v-cp4r3-01 p2-1xa-tls1-v-cp4r3-01 p2-2xa-tls1-v-cp4r3-01 p2-5xa-tls1-v-cp4r3-01 p1-1xa-tls1_1-v-cp4r3-01 p2-1xa-tls1_1-v-cp4r3-01 p2-2xa-tls1_1-v-cp4r3-01 p2-5xa-tls1_1-v-cp4r3-01 p1-1xa-tls1_2-v-cp4r3-01 p2-1xa-tls1_2-v-cp4r3-01 p2-2xa-tls1_2-v-cp4r3-01 p2-5xa-tls1_2-v-cp4r3-01 p1-1xa-tls1-v-cp5-01 p2-1xa-tls1-v-cp5-01 p2-2xa-tls1-v-cp5-01 p2-5xa-tls1-v-cp5-01 p1-1xa-tls1_1-v-cp5-01 p2-1xa-tls1_1-v-cp5-01 p2-2xa-tls1_1-v-cp5-01 p2-5xa-tls1_1-v-cp5-01 p1-1xa-tls1_2-v-cp5-01 p2-1xa-tls1_2-v-cp5-01 p2-2xa-tls1_2-v-cp5-01 p2-5xa-tls1_2-v-cp5-01 +set SERVER_TESTS=cp20 cp21 csp36r4 csp39 csp4 csp4r3 csp5 +set OPENSSL_DEBUG_MEMORY=on + +rem eOR %%t IN (%BASE_TESTS%) DO %TCLSH% %%t.try +rem FOR %%t IN (%PKCS7_COMPATIBILITY_TESTS%) DO %TCLSH% %%t.try +FOR %%t IN (%SERVER_TESTS%) DO %TCLSH% server.try %%t +FOR %%t IN (%CLIENT_TESTS%) DO %TCLSH% client.try %%t +set CVS_RSH=ssh +FOR %%t IN (%WINCLIENT_TESTS%) DO %TCLSH% wcli.try %%t +IF EXIST %TESTDIR%\%OTHER_DIR% %TCLSH% interop.try +IF EXIST %TESTDIR%\%OTHER_VERSION% ( + set OTHER_DIR=%OTHER_VERSION% + IF %ENGINE_NAME%==cryptocom ( + set ALG_LIST="gost2001:A gost2001:B gost2001:C" + set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" + ) ELSE ( + set ALG_LIST="gost2001:A gost2001:B gost2001:C" + set ENC_LIST="gost2001:A:1.2.643.2.2.31.3 gost2001:B:1.2.643.2.2.31.4 gost2001:C:1.2.643.2.2.31.2 gost2001:A:" + ) + %TCLSH% interop.try +) + +%TCLSH% calcstat %TESTDIR%\stats %TESTDIR%\test.result diff --git a/tcl_tests/server.try b/tcl_tests/server.try new file mode 100644 index 0000000..bc12638 --- /dev/null +++ b/tcl_tests/server.try @@ -0,0 +1,205 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] + + +package require ossltest + +if {$argc != 1} { + puts stderr "Usage $argv0 cipher-list-file" + exit 1 +} +array set protos { + SSLv2 -ssl2 + SSLv3 -ssl3 + TLSv1 -tls1 + TLSv1.1 -tls1_1 + TLSv1.2 -tls1_2 + "default" {} +} +get_hosts [lindex $argv 0] +cd $::test::dir +start_tests "TLS-соединение с сервером [lindex $argv 0]" + +if {[engine_name] eq "ccore"} { + array unset hosts gost94* +} + +array set suite_map { +CRYPTOPRO-DHGOST94-DSS-GOST89-STREAMGOST89 GOST94-GOST89-GOST89 +CRYPTOPRO-DHGOST94-DSS-NULL-GOST94 GOST94-NULL-GOST94 +CRYPTOPRO-DHGOST-DSS-GOST89-STREAMGOST89 GOST2001-GOST89-GOST89 +CRYPTOPRO-DHGOST-DSS-NULL-GOST94 GOST2001-NULL-GOST94 +} + +set CAhost lynx.lan.cryptocom.ru +set CAprefix /cgi-bin/autoca + +foreach alg [array names hosts] { + set alg2 [regsub {(gost\d+)cp} $alg {\1}] + set alg_fn [string map {":" "_"} $alg2] + set alg_short [regexp -inline {^[^:]+} $alg2] + set alg_ca [regexp -inline {^[^:]+} $alg] + + test -skip {[file exist ca_$alg_short.pem]} "Получить сертификат $alg_ca CA" { + getCAcert $CAhost $CAprefix $alg_ca + } 0 "ca_$alg_ca.pem" + + if {[array exists suites]} {array unset suites} + array set suites $hosts($alg) + foreach suite [array names suites] { + if {![regexp {(.+):(.+)} $suite => proto cs]} { + set cs $suite + set proto "default" + } + if {[info exists suite_map($cs)]} { + set mycs $suite_map($cs) + } else { + set mycs $cs + } + if {![regexp {(.+:\d+):(.*)} $suites($suite) x url servertype]} { + set servertype apache + set url $suites($suite) + } + if {$servertype eq "iis"} { + set failure "HTTP 403.7 - Forbidden: Client certificate required" + set failure_exit_code 0 + } else { + set failure "ssl handshake failure" + set failure_exit_code 1 + } + + regexp {(.+):(\d+)} $url dummy get_hostname get_port + + test "$suite статическая страница " { + grep "

" [openssl [concat s_client $protos($proto) \ + [list -cipher $mycs -CAfile ca_$alg_ca.pem -connect $url \ + -verify_return_error -verify 1 -ign_eof \ + << "GET /ssl_test.html HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "

Test SSL static page

\n" + + + test "$suite большая страница" { + grep "

" [openssl [concat s_client $protos($proto) \ + [list -cipher $mycs -CAfile ca_$alg_ca.pem -connect $url \ + -verify_return_error -verify 1 -ign_eof \ + << "GET /ssl_test_big.html HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "

Big test SSL static page

\n" + + + if {$servertype eq "iis"} { + test "$suite скрипт printenv.asp" { + grep "SERVER_PORT_SECURE:" [openssl \ + [concat s_client $protos($proto) \ + [list -cipher $mycs -CAfile ca_$alg_ca.pem\ + -connect $url -verify_return_error -verify 1 -ign_eof \ + << "GET /printenv.asp HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "SERVER_PORT_SECURE: 1\n" + } else { + test "$suite скрипт printenv" { + grep "SSL_CIPHER=" [openssl \ + [concat s_client $protos($proto) \ + [list -cipher $mycs -CAfile ca_$alg_ca.pem \ + -connect $url -verify_return_error -verify 1 -ign_eof \ + << "GET /cgi-bin/printenv HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "SSL_CIPHER=\"$cs\"\n" + } + + if {[string match *GOST2012* $suite]} { + set alg_cli_list "$alg gost2001:B gost2012_256:B gost2012_512:B" + } elseif {[string match *GOST2001* $suite]} { + set alg_cli_list "$alg gost2001:B" + } else { + set alg_cli_list $alg + } + + foreach alg_cli $alg_cli_list { + set alg_cli_fn [string map {":" "_"} $alg_cli] + set alg_cli_short [regexp -inline {^[^:]+} $alg_cli] + + test -skip {[file exist U_x_$alg_cli_fn/cert.pem]} "Получение клиентского сертификата $alg_cli" { + getCAAlgParams $CAhost $CAprefix $alg_cli_short + if {![makeUser U_x_$alg_cli_fn $alg_cli CN \ + "Test engine on [info hostname]"]} { + error "Request generation failed" + } + registerUserAtCA U_x_$alg_cli_fn $CAhost $CAprefix $alg_ca + file exists U_x_$alg_cli_fn/cert.pem + } 0 1 + + + test "$suite нет сертификата, статичеcкая страница" { + set out [openssl [concat s_client $protos($proto) \ + [list -msg -cipher $mycs -CAfile ca_$alg_ca.pem \ + -verify_return_error -verify 1 -connect $url -ign_eof \ + << "GET /ssl_auth_test.html HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + if {[regexp $failure $out match]} { + set match + } else { + set out + } + } $failure_exit_code $failure + + + test -skip {![file exists U_x_$alg_cli_fn/cert.pem]} \ + "$suite, есть сертификат, статическая страница" { + grep "

" [openssl [concat s_client $protos($proto) \ + [list -msg -cipher $mycs -cert U_x_$alg_cli_fn/cert.pem \ + -key U_x_$alg_cli_fn/seckey.pem -CAfile ca_$alg_ca.pem \ + -verify_return_error -verify 1 -connect $url -ign_eof \ + << "GET /ssl_auth_test.html HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "

Test SSL static page

\n" + + + if {$servertype eq "iis"} { + + test "$suite, нет сертификата, скрипт printenv_auth.asp" { + set out [openssl [concat s_client $protos($proto) \ + [list -msg -cipher $mycs -CAfile ca_$alg_ca.pem \ + -verify_return_error -verify 1 -connect $url -ign_eof \ + << "GET /printenv_auth.asp HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + if {[regexp $failure $out match]} { + set match + } else { + set out + } + } 0 $failure + + + test -skip {![file exists U_x_$alg_cli_fn/cert.pem]} \ + "$suite, есть сертификат, скрипт printenv_auth.asp" { + grep CERT_FLAGS [openssl [concat s_client $protos($proto) \ + [list -msg -cipher $mycs -cert U_x_$alg_cli_fn/cert.pem\ + -key U_x_$alg_cli_fn/seckey.pem -CAfile ca_$alg_ca.pem \ + -verify_return_error -verify 1 -connect $url -ign_eof \ + << "GET /printenv_auth.asp HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "CERT_FLAGS: 1\n" + + } else { + + test "$suite, нет сертификата, скрипт printenv" { + set out [openssl [concat s_client $protos($proto) \ + [list -cipher $mycs -CAfile ca_$alg_ca.pem \ + -verify_return_error -verify 1 -connect $url -ign_eof \ + << "GET /cgi-bin/printenv/auth HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + if {[regexp "ssl handshake failure" $out match]} { + set match + } else { + set out + } + } 1 "ssl handshake failure" + + test -skip {![file exists U_x_$alg_cli_fn/cert.pem]} \ + "$suite, есть сертификат, скрипт printenv" { + grep SSL_CLIENT_VERIFY [openssl \ + [concat s_client $protos($proto) \ + [list -cipher $mycs -cert U_x_$alg_cli_fn/cert.pem \ + -key U_x_$alg_cli_fn/seckey.pem -CAfile ca_$alg_ca.pem \ + -verify_return_error -verify 1 -connect $url -ign_eof \ + << "GET /cgi-bin/printenv/auth HTTP/1.1\nHost: $get_hostname\nConnection: close\n\n"]]] + } 0 "SSL_CLIENT_VERIFY=\"SUCCESS\"\n" + } + } + } +} +end_tests diff --git a/tcl_tests/smime.try b/tcl_tests/smime.try new file mode 100644 index 0000000..cd610ba --- /dev/null +++ b/tcl_tests/smime.try @@ -0,0 +1,173 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на команду smime" + +test "Creating CA 2001" { + makeCA ${testname}CA gost2001:A +} 0 1 + +test "Creating CA 2012" { + makeCA +} 0 1 + + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_smime_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012 + set ca_sign_alg hash_with_sign12_512 + } + * {set ::test::ca ${testname}CA + set ca_sign_alg hash_with_sign01_cp + } + } + +test "Creating user with signing key $alg" { + makeRegisteredUser $username $alg + + if {![file exists $username/req.pem]&&[file exists $username/cert.pem]} { + file delete $username/cert.pem + } + file exists $username/cert.pem +} 0 1 + +test -skip {![file exists $username/cert.pem]} -createsfiles [list sign.dat sign_$alg_fn.msg] "Signing a message without cert by $alg" { + makeFile sign.dat [string repeat "Test data to sign.\n" 100] + openssl "smime -sign -in sign.dat -text -out sign_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem -nocerts" + file isfile sign_$alg_fn.msg +} 0 1 + +test -skip {![file exist sign_$alg_fn.msg]} "Checking micalg param in signed without cert $alg message" { + regexp -- micalg="[micalg [alg_hash $alg]]" [grep micalg [getFile sign_$alg_fn.msg]] +} 0 1 + +test -createsfiles sign_$alg_fn.pem -skip {![file exist sign_$alg_fn.msg]} "Extracting PKCS7 from signed without cert $alg message" { + openssl "smime -pk7out -out sign_$alg_fn.pem -in sign_$alg_fn.msg" + file isfile sign_$alg_fn.pem +} 0 1 + + +test -skip {![file exists sign_$alg_fn.pem]} "Checking oids in pkcs7 struct" { + extract_oids sign_$alg_fn.pem PEM +} 0 [mkObjList [hash_long_name $alg] [hash_long_name $alg] "GOST R 34.11-2012 with 256 bit hash" "GOST R 34.11-2012 with 512 bit hash" "GOST R 34.11-94" "GOST 28147-89" [alg_long_name $alg]] +#[mkObjList [alg_hash $alg] [alg_hash $alg] hash_12_256 hash_12_512 hash_94 crypt89_cc [alg_id $alg]] +# hash_12_256 hash_12_512 hash_94 crypt89_cc are from sMIMECapabilities + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg without ca " { + grep Veri [openssl "smime -verify -text -in sign_$alg_fn.msg -out verified.txt -noverify -certfile $username/cert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg with ca" { + grep Veri [openssl "smime -verify -text -in sign_$alg_fn.msg -out verified.txt -certfile $username/cert.pem -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists sign_$alg_fn.msg]} -createsfiles [list bad_$alg_fn.msg verified.txt] "Verifying corrupted messages signed with $alg" { + set corrupted [getFile sign_$alg_fn.msg] + set index [string first "Test data" $corrupted ] + makeFile bad_$alg_fn.msg [string replace $corrupted $index [expr $index+9] "Bad data"] + grep Verification [openssl "smime -verify -text -in bad_$alg_fn.msg -out verified.txt -noverify -certfile $username/cert.pem"] +} 1 "Verification failure" + +test -skip {![file exists $username/cert.pem]} -createsfiles [list sign.dat sign_c_$alg_fn.msg] "Signing a message by $alg with cert" { + makeFile sign.dat [string repeat "Test data to sign.\n" 100] + openssl "smime -sign -in sign.dat -crlfeol -text -out sign_c_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem" + file isfile sign_c_$alg_fn.msg +} 0 1 + +test -skip {![file exist sign_c_$alg_fn.msg]} "Checking micalg param in signed with cert $alg message" { + regexp -- micalg="[micalg [alg_hash $alg]]" [grep micalg [getFile sign_c_$alg_fn.msg]] +} 0 1 + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert inside without ca" { + grep Veri [openssl "smime -verify -text -in sign_c_$alg_fn.msg -out verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert with ca" { + grep Veri [openssl "smime -verify -text -in sign_c_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" +test -skip {![file exists $username/cert.pem]} -createsfiles {sign.dat sign_op_$alg_fn.msg} "Signing a message by $alg with cert using opaque signing" { + makeFile sign.dat [string repeat "Test data to sign.\n" 100] + openssl "smime -sign -in sign.dat -text -out sign_op_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem -nodetach" + file isfile sign_op_$alg_fn.msg +} 0 1 + +test -createsfiles verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a message signed by $alg having cert inside without ca" { + grep Veri [openssl "smime -verify -text -in sign_op_$alg_fn.msg -out verified.txt -noverify"] +} 0 "Verification successful +" + +test -createsfiles verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a $alg opaque message with ca" { + grep Veri [openssl "smime -verify -text -in sign_op_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -createsfiles broken_op_$alg_fn.msg -skip {![file exists sign_op_$alg_fn.msg]} "Verifying broken $alg opaque message" { + set data [getFile sign_op_$alg_fn.msg] + regexp "(.*)\n\r?\n(.+)" $data match header encoded + set asnstruct [::base64::decode $encoded] + makeFile broken_op_$alg_fn.msg "$header\n\n[::base64::encode [regsub -all\ + "Test data" $asnstruct "Best data"]]" + grep Verification [openssl "smime -verify -text -in broken_op_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + + +test -createsfiles "sign_det_$alg_fn.msg" -skip {![file exists $username/cert.pem]||![file exists sign.dat]} "Creating detached $alg signature" { + openssl "smime -sign -binary -in sign.dat -out sign_det_$alg_fn.msg -signer $username/cert.pem -inkey $username/seckey.pem" + file exists sign_det_$alg_fn.msg +} 0 1 + +test -skip {![file exist sign_det_$alg_fn.msg]} "Checking micalg param in detached $alg signature" { + regexp -- micalg="[micalg [alg_hash $alg]]" [grep micalg [getFile sign_det_$alg_fn.msg]] +} 0 1 + +test -createsfiles sign_det_$alg_fn.pem -skip {![file exist sign_det_$alg_fn.msg]} "Extracting PKCS7 from signed $alg message" { + openssl "smime -pk7out -out sign_det_$alg_fn.pem -in sign_det_$alg_fn.msg" + file isfile sign_det_$alg_fn.pem +} 0 1 + +#We expect cryptocom oids because of cert signed by ca with Cryptocom algs +# Result sequence +# 1. digest +# 2. algorithm of CA key +# 3. algorithm of current key +# 4. algorithm of CA key +# 5. digest +# 6. digests from sMIMECapabilities +# 7. encryption from sMIMECapabilities +# 8. algorithm of current key +test -skip {![file exists sign_det_$alg_fn.pem]} "Checking oids in pkcs7 struct" { + extract_oids sign_det_$alg_fn.pem PEM +} 0 [mkObjList [hash_long_name $alg] [smime_hash_with_sign_long_name $ca_sign_alg] [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] [smime_hash_with_sign_long_name $ca_sign_alg] [hash_long_name $alg] "GOST R 34.11-2012 with 256 bit hash" "GOST R 34.11-2012 with 512 bit hash" "GOST R 34.11-94" "GOST 28147-89" [alg_long_name $alg]] + +test -skip {![file exists sign_det_$alg_fn.pem]} "Verifying detached $alg signature" { + grep Veri [openssl "smime -verify -content sign.dat -inform PEM -in sign_det_$alg_fn.pem -out verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists sign_det_$alg_fn.msg]} -createsfiles {bad.dat} "Verifying corrupted $alg detached signature" { + makeFile bad.dat [regsub Test [getFile sign.dat] Best] + grep Verification [openssl "smime -verify -content bad.dat -in sign_det_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + + +} +end_tests diff --git a/tcl_tests/smime2.try b/tcl_tests/smime2.try new file mode 100644 index 0000000..c3bdd12 --- /dev/null +++ b/tcl_tests/smime2.try @@ -0,0 +1,205 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Тесты на команду smime - вторая подпись" + +test "Creating CA" { + makeCA +} 0 1 + +makeFile signed2.dat "Test data for 2 signatures" + + +foreach length {256 512} { + +test "Creating users $length" { + makeRegisteredUser U_smime_1_$length gost2012_$length:A CN USER1_$length emailAddress test@cryptocom.ru + makeRegisteredUser U_smime_2_$length gost2012_$length:A CN USER2_$length emailAddress test@cryptocom.ru +} 0 1 + +test -createsfiles signed2_1_$length.asn "Signing in DER format with 1st signature" { + openssl "smime -sign -binary -outform der -inform der -nodetach -inkey U_smime_1_$length/seckey.pem -signer U_smime_1_$length/cert.pem -in signed2.dat -out signed2_1_$length.asn" + file isfile signed2_1_$length.asn +} 0 1 + +test -createsfiles signed2_2_$length.asn "Signing in DER format with 2nd signature" { + openssl "smime -resign -binary -outform der -inform der -nodetach -inkey U_smime_2_$length/seckey.pem -signer U_smime_2_$length/cert.pem -in signed2_1_$length.asn -out signed2_2_$length.asn" + file isfile signed2_2_$length.asn +} 0 1 + +test -createsfiles {was_signed.dat signer.certs} "Verifying signature" { + grep "Verif" [openssl "smime -verify -inform der -in signed2_2_$length.asn -noverify -signer signer.certs -out was_signed.dat"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +test -createsfiles signed2_1_$length\_op.msg "Signing opaque in S/MIME format with 1st signature" { + openssl "smime -sign -binary -nodetach -inkey U_smime_1_$length/seckey.pem -signer U_smime_1_$length/cert.pem -in signed2.dat -out signed2_1_$length\_op.msg" + file isfile signed2_1_$length\_op.msg +} 0 1 + +test -createsfiles signed2_2_$length\_op.msg "Signing opaque in S/MIME format with 2nd signature" { + openssl "smime -resign -binary -nodetach -inkey U_smime_2_$length/seckey.pem -signer U_smime_2_$length/cert.pem -in signed2_1_$length\_op.msg -out signed2_2_$length\_op.msg" + file isfile signed2_2_$length\_op.msg +} 0 1 + +test -createsfiles {was_signed.dat signer.certs} "Verifying opaque signature" { + grep "Verif" [openssl "smime -verify -inform smime -in signed2_2_$length\_op.msg -noverify -signer signer.certs -out was_signed.dat"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +test -createsfiles signed2_1_$length\_det.asn "Signing detached in DER format with 1st signature" { + openssl "smime -sign -binary -outform der -inkey U_smime_1_$length/seckey.pem -signer U_smime_1_$length/cert.pem -in signed2.dat -out signed2_1_$length\_det.asn" + file isfile signed2_1_$length\_det.asn +} 0 1 + +test -createsfiles signed2_2_$length\_det.asn "Signing detached in DER format with 2nd signature" { + openssl "smime -resign -binary -inkey U_smime_2_$length/seckey.pem -signer U_smime_2_$length/cert.pem -in signed2_1_$length\_det.asn -content signed2.dat -inform der -outform der -out signed2_2_$length\_det.asn" + file isfile signed2_2_$length\_det.asn +} 0 1 + +test -createsfiles {was_signed.dat signer.certs} "Verifying detached signature in DER format" { + grep "Verif" [openssl "smime -verify -in signed2_2_$length\_det.asn -noverify -signer signer.certs -out was_signed.dat -content signed2.dat -inform der"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert_asn[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert_asn$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +test -createsfiles signed2_1_$length.msg "Signing in S/MIME format with 1st signature" { + openssl "smime -sign -binary -inform der -inkey U_smime_1_$length/seckey.pem -signer U_smime_1_$length/cert.pem -in signed2.dat -out signed2_1_$length.msg" + file isfile signed2_1_$length.msg +} 0 1 + +test -createsfiles signed2_2_$length.msg "Signing in S/MIME format with 2nd signature" { + grep "SMIME" [openssl "smime -resign -binary -inkey U_smime_2_$length/seckey.pem -signer U_smime_2_$length/cert.pem -in signed2_1_$length.msg -inform smime -out signed2_2_$length.msg"] +} 0 "" + +test -createsfiles {was_signed.dat signer.certs} "Verifying signature" { + grep "Verif" [openssl "smime -verify -in signed2_2_$length.msg -noverify -signer signer.certs -out was_signed.dat -inform smime"] +} 0 {Verification successful +} + +test "Signed data is extracted correctly" { + string eq [getFile signed2.dat] [getFile was_signed.dat] +} 0 1 + +### Test extracted sertificates + +test "Extracting signer certificates" { + set i 0 + set subjs {} + set certs [regexp -all -inline -- {-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----} [getFile signer.certs]] + foreach cert $certs { + makeFile cert_smime[incr i].pem $cert + lappend subjs [grep subject [openssl "x509 -in cert_smime$i.pem -subject -noout"]] + } + lsort $subjs +} 0 "{subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER1_$length, emailAddress = test@cryptocom.ru +} {subject=C = RU, O = Cryptocom, OU = OpenSSL Team, CN = USER2_$length, emailAddress = test@cryptocom.ru +}" + +} + + +test "Resigning in DER format with a unsuitable key length 512" { + openssl "smime -resign -binary -inform der -nodetach -inkey U_smime_2_512/seckey.pem -signer U_smime_2_512/cert.pem -in signed2_1_256.asn" +} 1 "no matching digest" + +test "Resigning in DER format with a unsuitable key length 256" { + openssl "smime -resign -binary -inform der -nodetach -inkey U_smime_2_256/seckey.pem -signer U_smime_2_256/cert.pem -in signed2_1_512.asn" +} 1 "no matching digest" + +test "Resigning opaque in S/MIME format with a unsuitable key length 512" { + openssl "smime -resign -binary -nodetach -inkey U_smime_2_512/seckey.pem -signer U_smime_2_512/cert.pem -in signed2_1_256_op.msg" +} 1 "no matching digest" + +test "Resigning opaque in S/MIME format with a unsuitable key length 256" { + openssl "smime -resign -binary -nodetach -inkey U_smime_2_256/seckey.pem -signer U_smime_2_256/cert.pem -in signed2_1_512_op.msg" +} 1 "no matching digest" + +test "Resigning detached in DER format with a unsuitable key length 512" { + openssl "smime -resign -binary -inform der -inkey U_smime_2_512/seckey.pem -signer U_smime_2_512/cert.pem -in signed2_1_256_det.asn -content signed2.dat" +} 1 "no matching digest" + +test "Resigning detached in DER format with a unsuitable key length 256" { + openssl "smime -resign -binary -inform der -inkey U_smime_2_256/seckey.pem -signer U_smime_2_256/cert.pem -in signed2_1_512_det.asn -content signed2.dat" +} 1 "no matching digest" + +test "Resigning in S/MIME format with a unsuitable key length 512" { + openssl "smime -resign -binary -inkey U_smime_2_512/seckey.pem -signer U_smime_2_512/cert.pem -in signed2_1_256.msg" +} 1 "no matching digest" + +test "Resigning in S/MIME format with a unsuitable key length 256" { + openssl "smime -resign -binary -inkey U_smime_2_256/seckey.pem -signer U_smime_2_256/cert.pem -in signed2_1_512.msg" +} 1 "no matching digest" + + +end_tests + +#./load_engine smime -sign -binary -outform der -inform der -nodetach -inkey certs/fstek.key -signer certs/fstek.crt -out signed2 -in signed1 +#./load_engine smime -verify -inform der -in signed2 -noverify +#./load_engine smime -verify -inform der -in signed2 -noverify -signer sss +#cat sss +#history +#vim sss +#./load_engine x509 -in sss sss2 +#./load_engine x509 -in sss +#./load_engine x509 -in sss -subject -noout +#./load_engine x509 -in sss2 -subject -noout +#./load_engine smime -verify -inform der -in signed2 -noverify -signer sss -out qqq diff --git a/tcl_tests/smime_cs.try b/tcl_tests/smime_cs.try new file mode 100644 index 0000000..1a01451 --- /dev/null +++ b/tcl_tests/smime_cs.try @@ -0,0 +1,83 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на восместимость по подписи с эталонными серверами" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_smime_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca smimeCA-2012 + set ca_sign_alg hash_with_sign12_512 + } + * {set ::test::ca smimeCA + set ca_sign_alg hash_with_sign01_cp + } + } +set hosts [list tls-ref-cp21] +foreach hstname $hosts { + + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg without ca via cms" { + grep Veri [openssl_remote "$username sign_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/sign_$alg_fn.msg -out TESTPATH/verified.txt -noverify -certfile TESTPATH/$username/cert.pem" "smime"] +} 0 "Verification successful +" + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg with ca via cms" { + grep Veri [openssl_remote "$::test::ca sign_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/sign_$alg_fn.msg -out TESTPATH/verified.txt -certfile TESTPATH/$username/cert.pem -CAfile TESTPATH/$::test::ca/cacert.pem" "smime"] +} 0 "Verification successful +" + +test -skip {![file exists bad_$alg_fn.msg]} -createsfiles TESTPATH/verified.txt] "Verifying corrupted messages signed with $alg via cms" { + grep Verification [openssl_remote "bad_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/bad_$alg_fn.msg -out TESTPATH/verified.txt -noverify -certfile TESTPATH/$username/cert.pem" "smime"] +} 1 "Verification failure" + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert inside without ca via cms" { + grep Veri [openssl_remote "sign_c_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/sign_c_$alg_fn.msg -out TESTPATH/verified.txt -noverify" "smime"] +} 0 "Verification successful +" + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert with ca via cms" { + grep Veri [openssl_remote "sign_c_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/sign_c_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "smime"] +} 0 "Verification successful +" + +test -createsfiles TESTPATH/verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a message signed by $alg having cert inside without ca via cms" { + grep Veri [openssl_remote "sign_op_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/sign_op_$alg_fn.msg -out TESTPATH/verified.txt -noverify" "smime"] +} 0 "Verification successful +" + +test -createsfiles TESTPATH/verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a $alg opaque message with ca via cms" { + grep Veri [openssl_remote "sign_op_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/sign_op_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "smime"] +} 0 "Verification successful +" + +test -skip {![file exists broken_op_$alg_fn.msg]} "Verifying broken $alg opaque message" { + grep Verification [openssl_remote "broken_op_$alg_fn.msg" "$hstname" "cms -verify -text -in TESTPATH/broken_op_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "smime"] +} 1 "Verification failure" + +test -skip {![file exists sign_det_$alg_fn.pem]} "Verifying detached $alg signature via cms" { + grep Veri [openssl_remote "sign_det_$alg_fn.pem sign.dat" "$hstname" "cms -verify -content TESTPATH/sign.dat -inform PEM -in TESTPATH/sign_det_$alg_fn.pem -out TESTPATH/verified.txt -noverify" "smime"] +} 0 "Verification successful +" + +test -skip {![file exists sign_det_$alg_fn.msg] || ![file exists bad.dat]} "Verifying corrupted $alg detached signature" { + grep Verification [openssl_remote "sign_det_$alg_fn.msg bad.dat" "$hstname" "cms -verify -content TESTPATH/bad.dat -in TESTPATH/sign_det_$alg_fn.msg -out TESTPATH/verified.txt -CAfile TESTPATH/$::test::ca/cacert.pem" "smime"] +} 1 "Verification failure" +} + +} +end_tests diff --git a/tcl_tests/smime_io.try b/tcl_tests/smime_io.try new file mode 100644 index 0000000..ec4df2d --- /dev/null +++ b/tcl_tests/smime_io.try @@ -0,0 +1,80 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на совместимость smime и cms -sign" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + "open" {set alg_list {gost2001:A gost2001:B gost2001:C gost2012_256:A gost2012_256:B gost2012_256:C gost2012_512:A gost2012_512:B}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_smime_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca smimeCA-2012 + set ca_sign_alg hash_with_sign12_512 + } + * {set ::test::ca smimeCA + set ca_sign_alg hash_with_sign01_cp + } + } + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg without ca via cms" { + grep Veri [openssl "cms -verify -text -in sign_$alg_fn.msg -out verified.txt -noverify -certfile $username/cert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists sign_$alg_fn.msg]} "Verifying a message signed with $alg with ca via cms" { + grep Veri [openssl "cms -verify -text -in sign_$alg_fn.msg -out verified.txt -certfile $username/cert.pem -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists bad_$alg_fn.msg]} -createsfiles verified.txt "Verifying corrupted messages signed with $alg via smime" { + grep Verification [openssl "cms -verify -text -in bad_$alg_fn.msg -out verified.txt -noverify -certfile $username/cert.pem"] +} 1 "Verification failure" + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert inside without ca via cms" { + grep Veri [openssl "cms -verify -text -in sign_c_$alg_fn.msg -out verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists sign_c_$alg_fn.msg]} "Verifying a message signed with $alg having cert with ca via cms" { + grep Veri [openssl "cms -verify -text -in sign_c_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -createsfiles verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a message signed by $alg having cert inside without ca via cms" { + grep Veri [openssl "cms -verify -text -in sign_op_$alg_fn.msg -out verified.txt -noverify"] +} 0 "Verification successful +" + +test -createsfiles verified.txt -skip {![file exists sign_op_$alg_fn.msg]} "Verifying a $alg opaque message with ca via cms" { + grep Veri [openssl "cms -verify -text -in sign_op_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 0 "Verification successful +" + +test -skip {![file exists broken_op_$alg_fn.msg]} "Verifying broken $alg opaque message" { + grep Verification [openssl "cms -verify -text -in broken_op_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + +test -skip {![file exists sign_det_$alg_fn.pem]} "Verifying detached $alg signature via cms" { + grep Veri [openssl "cms -verify -content sign.dat -inform PEM -in sign_det_$alg_fn.pem -out verified.txt -noverify"] +} 0 "Verification successful +" + +test -skip {![file exists sign_det_$alg_fn.msg]} -createsfiles {bad.dat} "Verifying corrupted $alg detached signature" { + makeFile bad.dat [regsub Test [getFile sign.dat] Best] + grep Verification [openssl "cms -verify -content bad.dat -in sign_det_$alg_fn.msg -out verified.txt -CAfile $::test::ca/cacert.pem"] +} 1 "Verification failure" + +} +end_tests diff --git a/tcl_tests/smimeenc.try b/tcl_tests/smimeenc.try new file mode 100644 index 0000000..ea8be82 --- /dev/null +++ b/tcl_tests/smimeenc.try @@ -0,0 +1,185 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на smime -encrypt" +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} + +proc map {str list} { + set result {} + foreach a $list { + lappend result [subst $str] + } + return $result +} +if {![file exist encrypt.dat]} { + makeFile encrypt.dat [string repeat "Test data to encrypt.\n" 100] +} + +if {![info exist env(NO_RSA)]} { +test "Creating RSA CA" { + makeCA ${testname}CA-RSA rsa:512 +} 0 1 + +foreach user {U_enc_rsa_1 U_enc_rsa_2} { +test "Make registered user $user" { + makeRegisteredUser $user rsa:512 CAname ${testname}CA-RSA +} 0 1 +} + + +test "RSA User 1 encrypts message for RSA user 2" { + openssl "smime -encrypt -in encrypt.dat -des -out enc_rsa.msg U_enc_rsa_2/cert.pem" + file isfile enc_rsa.msg +} 0 1 + +test "RSA User 1 cannot decrypt message for RSA user 2" { + grep "Error" [openssl "smime -decrypt -in enc_rsa.msg -recip U_enc_rsa_1/cert.pem -inkey U_enc_rsa_1/seckey.pem"] +} 1 {Error decrypting PKCS#7 structure} + +test -createsfiles decrypt.rsa "RSA User 2 (with cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in enc_rsa.msg -recip U_enc_rsa_2/cert.pem -inkey U_enc_rsa_2/seckey.pem -out decrypt.rsa" + set result [getFile decrypt.rsa] + string eq $expected $result +} 0 1 + +test -createsfiles decrypt_nocert.rsa "RSA User 2 (without cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in enc_rsa.msg -inkey U_enc_rsa_2/seckey.pem -out decrypt_nocert.rsa" + set result [getFile decrypt_nocert.rsa] + string eq $expected $result +} 0 1 +} + +test "Creating CA 2001" { + makeCA ${testname}CA gost2001:A +} 0 1 + +test "Creating CA 2012" { + makeCA +} 0 1 + +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + } +} + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_enc_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012} + * {set ::test::ca ${testname}CA} + } + +test "Creating user $username with key $alg" { + makeRegisteredUser $username $alg + + if {![file exists $username/req.pem]&&[file exists $username/cert.pem]} { + file delete $username/cert.pem + } + file exists $username/cert.pem +} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createsfiles enc_$alg_fn.msg "Encrypting for $username" { + grep "rror" [openssl "smime -encrypt -in encrypt.dat -gost89 -out enc_$alg_fn.msg U_enc_$alg_fn/cert.pem"] +} 0 "" + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +test -createsfiles enc_$alg_fn.pem "Extracting PKCS7 from encrypted structure for $username" { + openssl "smime -pk7out -out enc_$alg_fn.pem -in enc_$alg_fn.msg" + file isfile enc_$alg_fn.pem +} 0 1 + +test -skip {![file exists enc_$alg_fn.pem]} "Checking oids in pkcs7 structure for $username" { + extract_oids enc_$alg_fn.pem PEM +} 0 [mkObjList [alg_long_name $alg] [pubkey_long_name $alg] [param_hash_long_name [param_hash $alg]] "GOST 28147-89" [encr_long_name $crypt_param]] + +test -createsfiles decrypt.$alg_fn "Decrypting file encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in enc_$alg_fn.msg -recip U_enc_$alg_fn/cert.pem -inkey U_enc_$alg_fn/seckey.pem -out decrypt.$alg_fn" + set result [getFile decrypt.$alg_fn] + string eq $expected $result +} 0 1 + +if {[string length $crypt_param]} { + set env(CRYPT_PARAMS) $crypt_param +} else { + if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} +} + +test -createsfiles enc_t_$alg_fn.msg "Encrypting for $username - text format" { + grep "rror" [openssl "smime -encrypt -text -in encrypt.dat -gost89 -out enc_t_$alg_fn.msg U_enc_$alg_fn/cert.pem"] +} 0 "" + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +test -createsfiles decrypt_t.$alg_fn "Decrypting file text-encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -text -in enc_t_$alg_fn.msg -recip U_enc_$alg_fn/cert.pem -inkey U_enc_$alg_fn/seckey.pem -out decrypt_t.$alg_fn" + set result [getFile decrypt_t.$alg_fn] + string eq $expected $result +} 0 1 + +test -createsfiles decrypt_t_nocert.$alg_fn "Decrypting file text-encrypted for $username without cert" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -text -in enc_t_$alg_fn.msg -inkey U_enc_$alg_fn/seckey.pem -out decrypt_t_nocert.$alg_fn" + set result [getFile decrypt_t_nocert.$alg_fn] + string eq $expected $result +} 0 1 +} + +if {[info exists env(CRYPT_PARAMS)]} {unset env(CRYPT_PARAMS)} + +# FIXME этот тест и парное расшифрование надо прогнать с разными параметрами шифрования +test -createfiles enc_4all "Encrypt for all" { + grep "rror" [openssl "smime -encrypt -in encrypt.dat -gost89 -out enc_4all.msg [map {U_enc_[make_fn $a]/cert.pem} $enc_list]"] +} 0 "" + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_enc_$alg_fn + +test -skip {![file exists enc_4all.msg]} -createsfiles decrypt_4all.$alg_fn "Decrypting gost-encrypted file, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in enc_4all.msg -recip $username/cert.pem -inkey $username/seckey.pem -out decrypt_4all.$alg_fn" + set result [getFile decrypt_4all.$alg_fn] + string eq $expected $result +} 0 1 + +test -skip {![file exists enc_4all.msg]} -createsfiles decrypt_4all_nocert.$alg_fn "Decrypting gost-encrypted file without cert, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "smime -decrypt -in enc_4all.msg -inkey $username/seckey.pem -out decrypt_4all_nocert.$alg_fn" + set result [getFile decrypt_4all_nocert.$alg_fn] + string eq $expected $result +} 0 1 +} + +restore_env2 {OPENSSL_CONF CRYPT_PARAMS} + +end_tests diff --git a/tcl_tests/smimeenc_io.try b/tcl_tests/smimeenc_io.try new file mode 100644 index 0000000..fa991ce --- /dev/null +++ b/tcl_tests/smimeenc_io.try @@ -0,0 +1,104 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set testname [file rootname [file tail $::argv0]] + +start_tests "Тесты на совместимость smime и cms -encrypt" +proc make_fn {alg} { + return [string map {":" "_"} $alg] +} + +proc map {str list} { + set result {} + foreach a $list { + lappend result [subst $str] + } + return $result +} + +if {![info exist env(NO_RSA)]} { + +test -createsfiles io_decrypt.rsa "RSA User 2 (with cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in enc_rsa.msg -recip U_enc_rsa_2/cert.pem -inkey U_enc_rsa_2/seckey.pem -out io_decrypt.rsa" + set result [getFile io_decrypt.rsa] + string eq $expected $result +} 0 1 + +test -createsfiles io_decrypt_nocert.rsa "RSA User 2 (without cert) can decrypt message for RSA user 2" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in enc_rsa.msg -inkey U_enc_rsa_2/seckey.pem -out io_decrypt_nocert.rsa" + set result [getFile io_decrypt_nocert.rsa] + string eq $expected $result +} 0 1 +} + +if {[info exist env(ENC_LIST)]} { + set enc_list $env(ENC_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + "open" {set enc_list {gost2001:XA:1.2.643.2.2.31.3 gost2001:XB:1.2.643.2.2.31.4 gost2001:XA: gost2012_256:XA:1.2.643.2.2.31.1 gost2012_256:XB:1.2.643.7.1.2.5.1.1 gost2012_256:XA: gost2012_512:A:1.2.643.2.2.31.3 gost2012_512:B:1.2.643.7.1.2.5.1.1 gost2012_512:A:}} + } +} + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_enc_$alg_fn + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012} + * {set ::test::ca ${testname}CA} + } + +test -createsfiles io_decrypt.$alg_fn "Decrypting file encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in enc_$alg_fn.msg -recip U_enc_$alg_fn/cert.pem -inkey U_enc_$alg_fn/seckey.pem -out io_decrypt.$alg_fn" + set result [getFile io_decrypt.$alg_fn] + string eq $expected $result +} 0 1 + +test -createsfiles io_decrypt_t.$alg_fn "Decrypting file text-encrypted for $username" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -text -in enc_t_$alg_fn.msg -recip U_enc_$alg_fn/cert.pem -inkey U_enc_$alg_fn/seckey.pem -out io_decrypt_t.$alg_fn" + set result [getFile io_decrypt_t.$alg_fn] + string eq $expected $result +} 0 1 + +test -createsfiles io_decrypt_t_nocert.$alg_fn "Decrypting file text-encrypted for $username without cert" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -text -in enc_t_$alg_fn.msg -inkey U_enc_$alg_fn/seckey.pem -out io_decrypt_t_nocert.$alg_fn" + set result [getFile io_decrypt_t_nocert.$alg_fn] + string eq $expected $result +} 0 1 +} + +foreach enc_tuple $enc_list { + if {![regexp {^([^:]*:[^:]*):(.*)$} $enc_tuple -> alg crypt_param]} { + set alg $enc_tuple + set crypt_param {} + } + set alg_fn [make_fn $enc_tuple] + set username U_enc_$alg_fn + +test -skip {![file exists enc_4all.msg]} -createsfiles io_decrypt_4all.$alg_fn "Decrypting gost-encrypted file, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in enc_4all.msg -recip $username/cert.pem -inkey $username/seckey.pem -out io_decrypt_4all.$alg_fn" + set result [getFile io_decrypt_4all.$alg_fn] + string eq $expected $result +} 0 1 + +test -skip {![file exists enc_4all.msg]} -createsfiles io_decrypt_4all_nocert.$alg_fn "Decrypting gost-encrypted file without cert, recipient $alg_fn" { + set expected [getFile encrypt.dat] + openssl "cms -decrypt -in enc_4all.msg -inkey $username/seckey.pem -out io_decrypt_4all_nocert.$alg_fn" + set result [getFile io_decrypt_4all_nocert.$alg_fn] + string eq $expected $result +} 0 1 +} + +end_tests diff --git a/tcl_tests/ssl.try b/tcl_tests/ssl.try new file mode 100644 index 0000000..54672bc --- /dev/null +++ b/tcl_tests/ssl.try @@ -0,0 +1,322 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +if {$argc==2} { + switch -exact -- [lindex $argv 0] { + -serverconf { + set test::server_conf [lindex $argv 1] + set test::suffix "-clt" + } + -clientconf { + set test::client_conf [lindex $argv 1] + set test::suffix "-srv" + } + default { + puts stderr "invalid command line" + exit 1; + } + } +} elseif $argc { + puts stderr "invalid command line" + exit 1 +} + +array set protos { + TLSv1 -tls1 + TLSv1.1 -tls1_1 + TLSv1.2 -tls1_2 +} + +cd $::test::dir + +start_tests "Тесты на SSL-соединение между s_client и s_server" + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A}} + "open" {set alg_list {rsa:1024 gost2001:XA gost2012_256:XA gost2012_512:A}} + } +} + +array set suites { +rsa:1024 {ECDHE-RSA-AES256-SHA} +gost2001:XA {GOST2001-GOST89-GOST89 GOST2001-NULL-GOST94@SECLEVEL=0 GOST2012-GOST8912-GOST8912 GOST2012-NULL-GOST12@SECLEVEL=0} +gost2012_256:XA {GOST2012-GOST8912-GOST8912 GOST2012-NULL-GOST12@SECLEVEL=0 GOST2012-MAGMA-MAGMAOMAC GOST2012-KUZNYECHIK-KUZNYECHIKOMAC} +gost2012_512:A {GOST2012-GOST8912-GOST8912 GOST2012-NULL-GOST12@SECLEVEL=0 GOST2012-MAGMA-MAGMAOMAC GOST2012-KUZNYECHIK-KUZNYECHIKOMAC} +} + +# +# Incompatible cipher suites +# +array set badsuites { +gost2012_256:XA {GOST2001-GOST89-GOST89 GOST2001-NULL-GOST94@SECLEVEL=0} +gost2012_512:A {GOST2001-GOST89-GOST89 GOST2001-NULL-GOST94@SECLEVEL=0} +} + +# +# Default cipher suite negotiated for algorithm +# +array set defsuite { +rsa:1024 ECDHE-RSA-AES256-SHA +#gost94:XA GOST94-GOST89-GOST89 +gost2001:XA GOST2012-GOST8912-GOST8912 +gost2012_256:XA GOST2012-GOST8912-GOST8912 +gost2012_512:A GOST2012-GOST8912-GOST8912 +} + +array set defsuite_12 { +rsa:1024 ECDHE-RSA-AES256-GCM-SHA384 +#gost94:XA GOST94-GOST89-GOST89 +gost2001:XA GOST2012-GOST8912-GOST8912 +gost2012_256:XA GOST2012-MAGMA-MAGMAOMAC +gost2012_512:A GOST2012-MAGMA-MAGMAOMAC +} + +set proto_list {"TLSv1" "TLSv1.1" "TLSv1.2"} + +if {![file exists sslCA/cacert.pem]} { + makeCA sslCA rsa +} else { + set ::test::ca sslCA +} + +test -skip {[file exist localhost_rsa/cert.pem]} \ + "Создаем серверный сертификат rsa" { + makeRegisteredUser localhost_rsa rsa:1024 CN localhost +} 0 1 + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + + test -skip {[file exist localhost_$alg_fn/cert.pem]} \ + "Создаем серверный сертификат $alg" { + makeRegisteredUser localhost_$alg_fn $alg CN localhost OU $alg_fn + } 0 1 + + test -skip {[file exists ssl_user_$alg_fn/cert.pem]} \ + "Создаем клиентский сертификат $alg" { + makeRegisteredUser ssl_user_$alg_fn $alg CN ssl_user OU $alg_fn + } 0 1 +} + +foreach alg {gost2001:B gost2012_256:B gost2012_512:B} { + set alg_fn [string map {":" "_"} $alg] + test -skip {[file exists ssl_user_$alg_fn/cert.pem]} \ + "Создаем клиентский сертификат $alg" { + makeRegisteredUser ssl_user_$alg_fn $alg CN ssl_user OU $alg_fn + } 0 1 +} + + +foreach proto $proto_list { + foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + + if {[string match *2012* $alg]} { + foreach suite $badsuites($alg) { + + test "Incompatible suite $alg $suite $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem \ + -verify_return_error -verify 1 -state -cipher $suite] \ + [list -www -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem -cipher $suite \ + $protos($proto)] {}] + list [lindex $list 2] [grep "^New," [lindex $list 0]] + } 0 [list 1 "New, (NONE), Cipher is (NONE)\n"] + } + } + + foreach suite $suites($alg) { + set raw_name [lindex [split $suite @] 0] + + if {![string equal $proto "TLSv1.2"] && [string match *OMAC* $suite]} { + continue + } + + if {[string equal $proto "TLSv1.2"] && [string match *OMAC* $suite]} { + set expected_proto "TLSv1.2" + } else { + set expected_proto "TLSv1.0" + } + + test "Запуск сервера $suite $proto" { + set f [open_server [list -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem -cipher $suite \ + $protos($proto)]] + stop_server $f + foreach {out err status} [stop $f] break + log "SERVER OUTPUT:\n$out\n----" + log "SERVER STDERR:\n$err\n----" + log "SERVER EXIT CODE: $status" + grep "ACCEPT" $out + } 0 "ACCEPT\n" + log $errorInfo + flush [test_log] + + test "Корректный хэндшейк $suite $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cipher $suite ] \ + [list -www -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem \ + -cipher $suite $protos($proto)] {}] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + list [lindex $list 2] $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $proto $raw_name] + + + test "Несовпадающий шиферсьют DHE-RSA-AES256-SHA $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cipher $suite] \ + [list -www -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem \ + -cipher DHE-RSA-AES256-SHA $protos($proto)] {}] + list [lindex $list 2] [grep ":fatal:" [lindex $list 1]] + } 0 [list 1 "SSL3 alert read:fatal:handshake failure +"] + + test "Получение странички $suite $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cipher $suite -ign_eof] \ + [list -www -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem -cipher $suite \ + $protos($proto)] "GET /\n\n"] + grep "^New," [lindex $list 0] + } 0 "New, $expected_proto, Cipher is $raw_name\nNew, $expected_proto, Cipher is $raw_name\n" + + if {![string match "*-NULL-*" $suite]} { + + test "Сервер поддерживающий много шиферсьютов $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cipher $suite] \ + [list -www -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem $protos($proto)] {}] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + list [lindex $list 2] $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $proto $suite] + + + test "Сервер c несколькими алгоритмами, клиент $suite $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cipher $suite] \ + [list -www -cert localhost_rsa/cert.pem \ + -key localhost_rsa/seckey.pem \ + -dcert localhost_$alg_fn/cert.pem \ + -dkey localhost_$alg_fn/seckey.pem $protos($proto)] {}] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + list [lindex $list 2] $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $proto $suite] + + } + + test "Сервер c несколькими алгоритмами, клиент AES256-SHA $proto" { + set list [client_server [list -connect localhost:4433 \ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cipher AES256-SHA] \ + [list -www -cert localhost_rsa/cert.pem \ + -key localhost_rsa/seckey.pem \ + -dcert localhost_$alg_fn/cert.pem \ + -dkey localhost_$alg_fn/seckey.pem $protos($proto)] {}] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + list [lindex $list 2] $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $proto AES256-SHA] + + + + if {[string match *gost* $alg]} { + set alg_cli_list [list $alg gost2001:B gost2012_256:B gost2012_512:B] + } else { + set alg_cli_list $alg + } + + foreach alg_cli $alg_cli_list { + set alg_cli_fn [string map {":" "_"} $alg_cli] + + test "Сервер $alg, клиент с сертификатом $alg_cli $proto" { + set list [client_server [list -connect localhost:4433\ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cert ssl_user_$alg_cli_fn/cert.pem \ + -key ssl_user_$alg_cli_fn/seckey.pem -cipher $suite \ + -ign_eof]\ + [list -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem -verify_return_error\ + -Verify 3 -www -CAfile $::test::ca/cacert.pem \ + -cipher $suite $protos($proto)] "GET /\n"] + list [lindex $list 2] [grep "^New," [lindex $list 0]] + } 0 [list 0 [string repeat "New, $expected_proto, Cipher is $raw_name\n" 2]] + + } + + } + + if {[string equal $proto "TLSv1.2"]} { + set etalon $defsuite_12($alg) + } else { + set etalon $defsuite($alg) + } + + if {[string equal $proto "TLSv1.2"] && ![string match *2001* $alg]} { + set expected_proto "TLSv1.2" + } else { + set expected_proto "TLSv1.0" + } + + test "Умолчательный хендшейк с ключами $alg $proto" { + set list [client_server [list -connect localhost:4433\ + -CAfile $::test::ca/cacert.pem -verify_return_error -verify 1\ + -state -ign_eof]\ + [list -www -cert localhost_$alg_fn/cert.pem\ + -key localhost_$alg_fn/seckey.pem $protos($proto)] "GET /\n"] + if {[regexp -lineanchor \ + {^\s*Protocol\s*:\s*(\S*)\s*$.*^\s*Cipher\s*:\s*(\S*)\s*$} \ + [lindex $list 0] -> result_proto result_cipher]} { + list [lindex $list 2] $result_proto $result_cipher + } else { + lindex $list 1 + } + } 0 [list 0 $proto $etalon] + + test "Умолчательный хендшейк с клиентской аутентификацией $alg $proto" { + set list [client_server [list -connect localhost:4433\ + -CAfile $::test::ca/cacert.pem -verify_return_error \ + -verify 1 -state -cert ssl_user_$alg_fn/cert.pem \ + -key ssl_user_$alg_fn/seckey.pem -ign_eof]\ + [list -cert localhost_$alg_fn/cert.pem \ + -key localhost_$alg_fn/seckey.pem -verify_return_error\ + -Verify 3 -www -CAfile $::test::ca/cacert.pem $protos($proto)] \ + "GET /\n"] + list [lindex $list 2] [grep "^New," [lindex $list 0]] + } 0 [list 0 [string repeat "New, $expected_proto, Cipher is $etalon\n" 2]] + + } +} + +end_tests diff --git a/tcl_tests/tc26_cms/encrypted_keyagree_a211.pem b/tcl_tests/tc26_cms/encrypted_keyagree_a211.pem new file mode 100644 index 0000000..9588e62 --- /dev/null +++ b/tcl_tests/tc26_cms/encrypted_keyagree_a211.pem @@ -0,0 +1,14 @@ +-----BEGIN CMS----- +MIIB/gYJKoZIhvcNAQcDoIIB7zCCAesCAQIxggFioYIBXgIBA6CBo6GBoDAXBggq +hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAe+itJVNbHM35RHfzuwFJPYdPXqtW +8hNEF7Z/XFEE2T71SRkhFX7ozYKQNh/TkVY9D4vG0LnD9Znr/pJyOjpsNb+dPcKX +Kbk/0JQxoPGHxFzASVAFq0ov/yBe2XGFWMeKUqtaAr7SvoYS0oEhT5EuT8BXmecd +nRe7NqOzESpb15ahIgQgsqHxOcdOp03l11S7k3OH1k1HNa5F8m9ctrOzH2846FMw +FwYJKoUDBwEBBwIBMAoGCCqFAwcBAQYCMHYwdDBAMDgxDTALBgNVBAoTBFRLMjYx +JzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIEAYy6hQQw +SxLc18zMwzLwXbcKqYhV/VzsdBgVArOHsSBIbaThJWE7zI37VGPMQJM5VXJ7GVcL +MF0GCSqGSIb3DQEHATAfBgkqhQMHAQEFAgIwEgQQ6EeVlADDCz2cdEWKy+tM94Av +yIFl/Ie4VeFFuczTsMsIaOUEe3Jn9GeVp8hZSj3O2q4hslQ/u/+Gj4QkSHm/M0ih +ITAfBgkqhQMHAQAGAQExEgQQs1t6D3J3WCEvxunnEE15NQ== +-----END CMS----- + diff --git a/tcl_tests/tc26_cms/encrypted_keyagree_a221.pem b/tcl_tests/tc26_cms/encrypted_keyagree_a221.pem new file mode 100644 index 0000000..b69ec04 --- /dev/null +++ b/tcl_tests/tc26_cms/encrypted_keyagree_a221.pem @@ -0,0 +1,10 @@ +-----BEGIN CMS----- +MIIBawYJKoZIhvcNAQcDoIIBXDCCAVgCAQIxgfehgfQCAQOgQjBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6gqEiBCBvcfyuSF57y8vVyaw8Z0ch3wjC4lPKTrpVRXty4Rhk5DAXBgkq +hQMHAQEHAQEwCgYIKoUDBwEBBgEwbjBsMEAwODENMAsGA1UEChMEVEsyNjEnMCUG +A1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0AgQBjLqDBChPbi6B +krXuLPexPAL2oUGCFWDGQHqINL5ExuMBG7/5XQRqriKARVa0MFkGCSqGSIb3DQEH +ATAbBgkqhQMHAQEFAQEwDgQMdNdCKnYAAAAwqTEDgC9O2bYyTGQJ8WUQGq0zHwzX +L0jFhWHTF1tcAxYmd9pX5i89UwIxhtYqyjX1QHju2g== +-----END CMS----- diff --git a/tcl_tests/tc26_cms/encrypted_keytrans_a231.pem b/tcl_tests/tc26_cms/encrypted_keytrans_a231.pem new file mode 100644 index 0000000..769457d --- /dev/null +++ b/tcl_tests/tc26_cms/encrypted_keytrans_a231.pem @@ -0,0 +1,11 @@ +-----BEGIN CMS----- +MIIBlQYJKoZIhvcNAQcDoIIBhjCCAYICAQAxggEcMIIBGAIBADBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6gzAXBgkqhQMHAQEHAgEwCgYIKoUDBwEBBgEEgbcwgbQEMFiMredFR3Mv +3g2wqyVXRnrhYEBMNFaqqgBpHwPQh3bF98tt9HZPxRDCww0OPfxeuTBeMBcGCCqF +AwcBAQEBMAsGCSqFAwcBAgEBAQNDAARAdFJ9ww+3ptvQiaQpizCldNYhl4DB1rl8 +Fx/2FIgnwssCbYRQ+UuRsTk9dfLLTGJG3JIEXKFxXWBgOrK965A5pAQg9f2/EHxG +DfetwCe1a6uUDCWD+wp5dYOpfkry8YRDEJgwXQYJKoZIhvcNAQcBMB8GCSqFAwcB +AQUCATASBBDUHNxmVclO/v3OaY9P7jxOgC+sD9CHGlEMRUpfGn6yfFDMExmYeby8 +LzdPJe1MkYV0qQgdC1zI3nQ7/4taf+4zRA== +-----END CMS----- diff --git a/tcl_tests/tc26_cms/encrypted_keytrans_a241.pem b/tcl_tests/tc26_cms/encrypted_keytrans_a241.pem new file mode 100644 index 0000000..f340561 --- /dev/null +++ b/tcl_tests/tc26_cms/encrypted_keytrans_a241.pem @@ -0,0 +1,13 @@ +-----BEGIN CMS----- +MIIB7wYJKoZIhvcNAQcDoIIB4DCCAdwCAQAxggFfMIIBWwIBADBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6hTAXBgkqhQMHAQEHAQEwCgYIKoUDBwEBBgIEgfowgfcEMIjuwfzfONBU +93ktK8OrTr22Q/1IMWaVc2JZxzxoQP5nzT4fq5aUBsY44bEdPhYgRzCBoDAXBggq +hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAcaoBV0T9iLYdvUbyw2g6E/mIXerg +AUAHwAOoXu88GUsTclujKTRgBUDWoRQkEZTPVvPZ58Tx3JWsy2JxJQppwbr9KaoE +PhnxivZJ2PYG0cK8B/OHiq30I9KwHyGArA3RK4SD2HqoIxeSRWa5IyhoAxSfg5YY +5LWTXxsNiX+lH9cEIIwDwI/cy7SphgEFu76rkjirn2JHdWiXLTKk4SdG/R+wMFkG +CSqGSIb3DQEHATAbBgkqhQMHAQEFAQIwDgQM1k2BxkeBXYCEsGMvgC9qSqe1z1h4 +3DHBdQRofT56xVOZCi+JxzUbPqKNzU3vjU2W/LlYgOLGuTJwEzw7GaEZMBcGCSqF +AwcBAAYBATEKBAgvK0+i46xDsw== +-----END CMS----- diff --git a/tcl_tests/tc26_cms/encrypted_kuznyechik_a421.pem b/tcl_tests/tc26_cms/encrypted_kuznyechik_a421.pem new file mode 100644 index 0000000..09aa0fc --- /dev/null +++ b/tcl_tests/tc26_cms/encrypted_kuznyechik_a421.pem @@ -0,0 +1,5 @@ +-----BEGIN CMS----- +MHEGCSqGSIb3DQEHBqBkMGICAQAwXQYJKoZIhvcNAQcBMB8GCSqFAwcBAQUCATAS +BBBSwX+zyOEPPuGyfpsRG4AigC/P8ftTdQMStfIThVkE/vpJlwaHgGv83m2bsPay +eyuqpoTeEMOaqGcO0MxHWsC9hQ== +-----END CMS----- diff --git a/tcl_tests/tc26_cms/encrypted_magma_a411.pem b/tcl_tests/tc26_cms/encrypted_magma_a411.pem new file mode 100644 index 0000000..15f1e6e --- /dev/null +++ b/tcl_tests/tc26_cms/encrypted_magma_a411.pem @@ -0,0 +1,5 @@ +-----BEGIN CMS----- +MIGIBgkqhkiG9w0BBwagezB5AgEAMFkGCSqGSIb3DQEHATAbBgkqhQMHAQEFAQIw +DgQMuncOu3uYPbI30vFCgC9Nsws4R09yLp6jUtadncWUPZGmCGpPKnXGgNHvEmUA +rgKJvu4FPHtLkHuLeQXZg6EZMBcGCSqFAwcBAAYBATEKBAjCbQoH632oGA== +-----END CMS----- diff --git a/tcl_tests/tc26_cms/encryption_key.hex b/tcl_tests/tc26_cms/encryption_key.hex new file mode 100644 index 0000000..76f3bdf --- /dev/null +++ b/tcl_tests/tc26_cms/encryption_key.hex @@ -0,0 +1 @@ +8F5EEF8814D228FB2BBC5612323730CFA33DB7263CC2C0A01A6C6953F33D61D5 diff --git a/tcl_tests/tc26_cms/hashed_a311.pem b/tcl_tests/tc26_cms/hashed_a311.pem new file mode 100644 index 0000000..8dce781 --- /dev/null +++ b/tcl_tests/tc26_cms/hashed_a311.pem @@ -0,0 +1,5 @@ +-----BEGIN CMS----- +MH0GCSqGSIb3DQEHBaBwMG4CAQAwCgYIKoUDBwEBAgIwOwYJKoZIhvcNAQcBoC4E +LMru7fLw7uv87fvpIO/w6Ozl8CDk6/8g8fLw8+ry8/D7IERpZ2VzdERhdGEuBCD/ +esPQYsGkzxZV8uUMIAWt6SI8KtxBP8NyG8AGbJ8i/Q== +-----END CMS----- diff --git a/tcl_tests/tc26_cms/hashed_a321.pem b/tcl_tests/tc26_cms/hashed_a321.pem new file mode 100644 index 0000000..685a598 --- /dev/null +++ b/tcl_tests/tc26_cms/hashed_a321.pem @@ -0,0 +1,6 @@ +-----BEGIN CMS----- +MIGfBgkqhkiG9w0BBwWggZEwgY4CAQAwCgYIKoUDBwEBAgMwOwYJKoZIhvcNAQcB +oC4ELMru7fLw7uv87fvpIO/w6Ozl8CDk6/8g8fLw8+ry8/D7IERpZ2VzdERhdGEu +BEDe4VUvcKSRvU7RFVhFjajXY+nJSUkUsoi3oOeJBnru4PErt8RusPrCJs614ciH +CM+ehrC4a+M1Nbq77F/Wsa/v +-----END CMS----- diff --git a/tcl_tests/tc26_cms/recipient256_cert.pem b/tcl_tests/tc26_cms/recipient256_cert.pem new file mode 100644 index 0000000..43d5486 --- /dev/null +++ b/tcl_tests/tc26_cms/recipient256_cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8jCCAZ+gAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD +VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBoMCEGCCqFAwcB +AQEBMBUGCSqFAwcBAgEBAQYIKoUDBwEBAgIDQwAEQL8nghlzLGMKWHuWhNMPMN5u +L6SkGqRiJ6qZxZb+4dPKbBT9LNVvNKtwUed+BeE5kfqOfolPgFusnL1rnO9yREOj +gYUwgYIwYQYDVR0BBFowWIAUgNkM95n4Zk2TU4mOwJ5xLaenMRKhOjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXSCBAGMuoEwHQYDVR0OBBYEFLue+PUb9Oe+pziBU+MvNejjgrzFMAoGCCqFAwcB +AQMCA0EAPP9Oad1/5jwokSjPpccsQ0xCdVYM+mGQ0IbpiZxQj8gnkt8sq4jR6Ya+ +I/BDkbZNDNE27TU1p3t5rE9NMEeViA== +-----END CERTIFICATE----- diff --git a/tcl_tests/tc26_cms/recipient256_key.pem b/tcl_tests/tc26_cms/recipient256_key.pem new file mode 100644 index 0000000..15c291b --- /dev/null +++ b/tcl_tests/tc26_cms/recipient256_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEgCAQAwIQYIKoUDBwEBAQEwFQYJKoUDBwECAQEBBggqhQMHAQECAgQgzre/jOVK +ur0vHHsZfkIQhvXkUYzK8cOrSxG88h/cyA0= +-----END PRIVATE KEY----- diff --git a/tcl_tests/tc26_cms/recipient512_cert.pem b/tcl_tests/tc26_cms/recipient512_cert.pem new file mode 100644 index 0000000..a26c9c4 --- /dev/null +++ b/tcl_tests/tc26_cms/recipient512_cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICNTCCAeKgAwIBAgIEAYy6hTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD +VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBqjAhBggqhQMH +AQEBAjAVBgkqhQMHAQIBAgEGCCqFAwcBAQIDA4GEAASBgKauwGYvUkzz19g0LP/p +zeRdmwy1m+QSy9W5ZrL/AGuJofm2ARjz40ozNbW6bp9hkHu8x66LX7u5zz+QeS2+ +X5om18UXriComgO0+qhZbc+Hzu0eQ8FjOd8LpLk3TzzfBltfLOX5IiPLjeum+pSP +0QjoXAVcrop//B4yvZIukvROo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJ +jsCecS2npzESoTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjog +R09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBSrXT5VKhm/5uff +kwW0XpG19k6AajAKBggqhQMHAQEDAgNBAAJBpsHRrQKZGb22LOzaReEB8rl2MbIR +ja64NaM5h+cAFoHm6t/k+ziLh2A11rTakR+5of4NQ3EjEhuPtomP2tc= +-----END CERTIFICATE----- diff --git a/tcl_tests/tc26_cms/recipient512_key.pem b/tcl_tests/tc26_cms/recipient512_key.pem new file mode 100644 index 0000000..f7e71c8 --- /dev/null +++ b/tcl_tests/tc26_cms/recipient512_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRA3HXF+fFK +6VIJkvHOEhfbcu8ZFQ/bzSS/jLOlrVbAV4fx0YjznHwK387PilH91kv4sWIaWblO +W8B/fAofmBUDpQ== +-----END PRIVATE KEY----- diff --git a/tcl_tests/tc26_cms/root256_cert.pem b/tcl_tests/tc26_cms/root256_cert.pem new file mode 100644 index 0000000..296fc36 --- /dev/null +++ b/tcl_tests/tc26_cms/root256_cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8DCCAZ2gAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYD +VQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwaDAhBggqhQMHAQEB +ATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABEAaSoKcjw54UACci6svELNF0IYM +RIW8urUsqamIpoG46XCqrVOuI6Q13N4dwcRsbZdqByf+GC2f5ZfO3baN5bTKo4GF +MIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzESoTowODENMAsGA1UE +ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0 +ggQBjLqBMB0GA1UdDgQWBBSA2Qz3mfhmTZNTiY7AnnEtp6cxEjAKBggqhQMHAQED +AgNBAAgv248F4OeNCkhlzJWec0evHYnMBlSzk1lDm0F875B7CqMrKh2MtJHXenbj +Gc2uRn2IwgmSf/LZDrYsKKqZSxk= +-----END CERTIFICATE----- diff --git a/tcl_tests/tc26_cms/root256_key.pem b/tcl_tests/tc26_cms/root256_key.pem new file mode 100644 index 0000000..15c291b --- /dev/null +++ b/tcl_tests/tc26_cms/root256_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEgCAQAwIQYIKoUDBwEBAQEwFQYJKoUDBwECAQEBBggqhQMHAQECAgQgzre/jOVK +ur0vHHsZfkIQhvXkUYzK8cOrSxG88h/cyA0= +-----END PRIVATE KEY----- diff --git a/tcl_tests/tc26_cms/sender256_cert.pem b/tcl_tests/tc26_cms/sender256_cert.pem new file mode 100644 index 0000000..a4694ae --- /dev/null +++ b/tcl_tests/tc26_cms/sender256_cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD +VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwaDAhBggqhQMH +AQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABECWKQ0TYllqg4GmY3tBJiyz +pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT +o4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzESoTowODENMAsG +A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt +Yml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJC62n2zu5Njd03zAKBggqhQMH +AQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4XWZ70FtsNlVxWATyzgO5Wliwn +t1O4GoZsxx8r6T/i7VG65UNmQlwdOKQ= +-----END CERTIFICATE----- diff --git a/tcl_tests/tc26_cms/sender256_key.pem b/tcl_tests/tc26_cms/sender256_key.pem new file mode 100644 index 0000000..3bac374 --- /dev/null +++ b/tcl_tests/tc26_cms/sender256_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEgCAQAwIQYIKoUDBwEBAQEwFQYJKoUDBwECAQEBBggqhQMHAQECAgQg6HfztrYQ +Y/WOBUo0ORFCMsUWevdvbLfDx3iZRA6BIAs= +-----END PRIVATE KEY----- diff --git a/tcl_tests/tc26_cms/sender512_cert.pem b/tcl_tests/tc26_cms/sender512_cert.pem new file mode 100644 index 0000000..6a9cd39 --- /dev/null +++ b/tcl_tests/tc26_cms/sender512_cert.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICNjCCAeOgAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD +VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaowIQYIKoUD +BwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwOBhAAEgYC0i7davCkOGGVcYqFP +tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts +/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4 +hMts9HYLnxbwJDMNVGuIHV6gzqOBhTCBgjBhBgNVHQEEWjBYgBSA2Qz3mfhmTZNT +iY7AnnEtp6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6 +IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUK+l9HAscONGx +zCcRpxRAmFHvlXowCgYIKoUDBwEBAwIDQQAbjA0Q41/rIKOOvjHKsAsoEJM+WJf6 +/PKXg2JaStthmw99bdtwwkU/qDbcje2tF6mt+XWyQBXwvfeES1GFY9fJ +-----END CERTIFICATE----- diff --git a/tcl_tests/tc26_cms/sender512_key.pem b/tcl_tests/tc26_cms/sender512_key.pem new file mode 100644 index 0000000..8acf4d7 --- /dev/null +++ b/tcl_tests/tc26_cms/sender512_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MGgCAQAwIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRAEWkl+ebl +sHWs86SNgRKqSxMOgGhbvR/uZ5/WWfdNG1axvUwVhpcXIxDZUmzQuNzqJBkseI7f +5/JjXyTFRF1a+Q== +-----END PRIVATE KEY----- diff --git a/tcl_tests/tc26_cms/signed_a111.pem b/tcl_tests/tc26_cms/signed_a111.pem new file mode 100644 index 0000000..1e2b0bc --- /dev/null +++ b/tcl_tests/tc26_cms/signed_a111.pem @@ -0,0 +1,25 @@ +-----BEGIN CMS----- +MIIENwYJKoZIhvcNAQcCoIIEKDCCBCQCAQExDDAKBggqhQMHAQECAzA7BgkqhkiG +9w0BBwGgLgQsyu7t8vDu6/zt++kg7/Do7OXwIOTr/yDx8vDz6vLz8PsgU2lnbmVk +RGF0YS6gggI6MIICNjCCAeOgAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRU +SzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQw +gaowIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwOBhAAEgYC0i7da +vCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDP +s2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo +6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzqOBhTCBgjBhBgNVHQEEWjBYgBSA +2Qz3mfhmTZNTiY7AnnEtp6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMT +HkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU +K+l9HAscONGxzCcRpxRAmFHvlXowCgYIKoUDBwEBAwIDQQAbjA0Q41/rIKOOvjHK +sAsoEJM+WJf6/PKXg2JaStthmw99bdtwwkU/qDbcje2tF6mt+XWyQBXwvfeES1GF +Y9fJMYIBlDCCAZACAQEwQDA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBU +SzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoQwCgYIKoUDBwEBAgOgga0w +GAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMzIw +MTk1NTIyWjAiBgkqhkiG9w0BCWIxFQQTU2lnbmVkIGF0dHIncyB2YWx1ZTBPBgkq +hkiG9w0BCQQxQgRAUdPHEukF5BIfo9DoQIMdnB0ZLkzq0RueEUZSNv07A7C+GKWi +G62fueArg8uPCHPTUN6d/42p33fgMkEwH7f7cDAKBggqhQMHAQEBAgSBgGUnVka8 +FvTlClmOtj/FUUacBdE/nEBeMLOO/535VDYrXlftPE6zQf/4ghS7TQG2VRGQ3GWD ++L3+W09A7d5uyyTEbvgtdllUG0OyqFwKmJEaYsMin87SFVs0cn1PGV1fOKeLluZa +bLx5whxd+mzlpekL5i6ImRX+TpERxrA/xSe5 +-----END CMS----- diff --git a/tcl_tests/tc26_cms/signed_a121.pem b/tcl_tests/tc26_cms/signed_a121.pem new file mode 100644 index 0000000..fe6d6b7 --- /dev/null +++ b/tcl_tests/tc26_cms/signed_a121.pem @@ -0,0 +1,19 @@ +-----BEGIN CMS----- +MIIDAQYJKoZIhvcNAQcCoIIC8jCCAu4CAQExDDAKBggqhQMHAQECAjA7BgkqhkiG +9w0BBwGgLgQsyu7t8vDu6/zt++kg7/Do7OXwIOTr/yDx8vDz6vLz8PsgU2lnbmVk +RGF0YS6gggH3MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRU +SzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQw +aDAhBggqhQMHAQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABECWKQ0TYllq +g4GmY3tBJiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZ +huJaJfqZ6VbTo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzES +oTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4x +MC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJC62n2zu5Njd0 +3zAKBggqhQMHAQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4XWZ70FtsNlVxW +ATyzgO5Wliwnt1O4GoZsxx8r6T/i7VG65UNmQlwdOKQxgaIwgZ8CAQEwQDA4MQ0w +CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1 +Ni1iaXQCBAGMuoIwCgYIKoUDBwEBAgIwCgYIKoUDBwEBAQEEQC6jZPA59szL9FiA +0wC71EBE42ap6gKxklT800cu2FvbLu972GJYNSI7+UeanVU37OVWyenEXi2E5HkU +94kBe8Q= +-----END CMS----- diff --git a/tcl_tests/test.tcl b/tcl_tests/test.tcl new file mode 100644 index 0000000..8e2b45a --- /dev/null +++ b/tcl_tests/test.tcl @@ -0,0 +1,452 @@ +# -*- coding: cp1251 -*- +# Установка номера тестового ПРА + +namespace eval vizir { + set regnumPRA 0000000000000001 +} + +# +# +# Собственно тестовый фреймворк + + + +namespace eval test { + # Уровень логгинга по умолчанию. Может быть переопределен явным + # присваиванием перед созданием контекста. Действует на контексты + # созданные makeCtx, makeCtx2 и threecontexts. + # Задание -logminpriority в test::ctxParams имеет приоритет. + set logLevel 3 + # Переменная хранящая имя динамической библиотеки для userlib + variable userlib {} + # Чтобы timestamp была определена всегда + variable timestamp [clock seconds] + proc findUserLib {} { + variable userlib + if {$::tcl_platform(platform)!="dos"} { + set dirlist [list [file dirname [info script]]\ + [file dirname [info nameofexecutable]]] + if {$::tcl_platform(platform) == "windows"} { + lappend dirlist\ + [file normalize [file join [file dirname [info script]] .. obj_mid.w32]]\ + [file normalize [file join [file dirname [info script]] .. obj_mid.w32]] + } elseif {$::tcl_platform(os) == "Linux"} { + lappend dirlist\ + [file normalize [file join [file dirname [info script]] .. obj_sid.lnx]] + } elseif {$::tcl_platform(os) == "SunOS"} { + if {$::tcl_platform(wordSize) == 8} { + set for s64 + } elseif {$::tcl_platform(byteOrder) == "littleEndian"} { + set for s86 + } else { + set for s32 + } + lappend dirlist\ + [file normalize [file join [file dirname [info script]] .. obj_sid.$for]] + } + foreach dir $dirlist { + set userlib_file [file join $dir usermci[info sharedlibextension]] + if {[file exists $userlib_file]} { + break + } + } + if {![file exists $userlib_file]} { + error "No usable userlib found in $dirlist" + } + set userlib [list -userlib $userlib_file] + } else { + set userlib {} + } + } + # + # + # + # Вызывается в начале тестового скрипта. Инициализирует необходимые + # переменные пакета, открывает лог и пишет в него заголовок + # Параметры name - заголовок тестового скрипта. + # + # Побочные эффекты - создается <имя-скрипта>.log + # + proc start_tests {name} { + variable suffix + if {![info exists suffix]} { + set binary [file rootname [file tail [info nameofexecutable]]] + if {$binary != "tclsh"} { + set suffix "_[string range [file tail [info nameofexecutable]] 0 2]" + } else { + set suffix "" + } + } + variable logname [file rootname [file tail [info script]]]$suffix.log + variable no 0 ok 0 failed 0 p_skip 0 c_skip 0 t_name $name logchannel [open $logname w] tempfiles {} + if {![catch {package present Vizir}]} { + findUserLib + } + puts [format [rus "=========== Группа тестов: %s ================="] [rus $name]] + puts $::test::logchannel [format [rus "Группа тестов \"%s\""] $name] + } + # + # Завершает выполнение теста и выводит отчет + # Вызывает exit + # + proc end_tests {} { + variable no + variable ok + variable failed + variable p_skip + variable t_name + variable c_skip + variable logname + variable tempfiles + variable suffix + puts "===================================================" + puts [format [rus "Всего %d тестов. Выполнено %d успешно, %d неуспешно"] $no $ok $failed] + if {$p_skip || $c_skip} { + puts [format [rus "Пропущено: %d на данной платформе %d из-за невыполнения других тестов"] $p_skip $c_skip] + } + if {$failed} { + puts [format [rus "Смотри более подробную информацию в %s"] $logname] + } + set test_id [file rootname [file tail [info script]]]$suffix + set stat [open "stats" a] + fconfigure $stat -encoding cp1251 + puts $stat [list $test_id [rus $t_name] $no $ok $failed $p_skip $c_skip] + close $stat + if {!$failed} { + foreach file $tempfiles { + + if [info exists $file] {puts [test_log] "Deleting $file" + file delete $file} + } + } + } + # + # Вовзращает идентификатор канала, куда пишется лог тестов. + # Рекомендуется назначать его в качестве -logchannel создаваемым + # контекстам чтобы вся выдача была в одном месте + # + proc test_log {} { + variable logchannel + return $logchannel + } + # + # Собственно тест + # Параметры + # 1. Название теста + # 2. Код (рекомендуется писать { + # код + # } + # 3. Ожидаемый результат выполнения - 0 успешно 1 - ошибка. Варианты + # больше 1 (TCL_BREAK, TCL_CONTINUE и TCL_RETURN) возможны, но вряд + # ли интересны + # 4. Ожидаемый возвращаемый результат + # Если предыдущий параметр 0, результат сравнивается на точное + # совпадение, если 1 - результат - регексп, которому должно + # удовлетворять сообщение об ошибке. + proc test args { + array set opts {} + variable tempfiles + variable timestamp + while {[string match -* [lindex $args 0]]} { + set key [lindex $args 0] + set val [lindex $args 1] + set args [lrange $args 2 end] + set opts($key) $val + } + foreach {message code exitStatus expectedResult} $args break + global errorInfo + if {[info exists opts(-platform)] && [lsearch -exact $opts(-platform) $::tcl_platform(platform)]==-1} { + logskip $message "platform" + return + } + if {[info exists opts(-platformex)] && ![uplevel expr $opts(-platformex)]} { + logskip $message "platform" + return + } + if {[info exists opts(-skip)] && [uplevel expr $opts(-skip)]} { + logskip $message "prereq" + return + } + if {[info exists opts(-fixme)] && [uplevel expr $opts(-fixme)]} { + logmiss $message "FIXME" + return + } + if {[info exists opts(-createsfiles)]} { + foreach file $opts(-createsfiles) { + lappend tempfiles $file + if {[file exists $file]} {file delete $file} + } + } + if {[info exists opts(-createsvars)]} { + foreach var $opts(-createsvars) { + uplevel "if {\[info exists $var\]} {unset $var}" + } + } + logbegin $message + set teststart [clock seconds] + set status [catch {uplevel $code} result] + set testend [clock seconds] + if {$teststart == $testend} { + set timestamp $teststart + } else { + # Handle negative intervals correctly + if {$teststart > $testend} { + set timestamp "$testend+[expr $teststart-$testend]" + } else { + set timestamp "$teststart+[expr $testend-$teststart]" + } + } + if {$status!=$exitStatus || ($status==1?![regexp --\ + [rus $expectedResult] $result]:([info exists opts(-time)]?\ + ![listcompare $result $expectedResult $opts(-time)]:\ + [string compare "$result" "$expectedResult"]))} { + logend "failed" + if {$status == 1} { + set expectedResult [rus $expectedResult] + } + log "Code:----$code---------------" + log "Expected status $exitStatus got $status" + log "Expected result: [list $expectedResult]" + log " Got result: [list $result]" + if {$status == 1} { + log "errorCode = $::errorCode" + } + } else { + logend "ok" + } + } +# +# Внутренние (неэкспортируемые)процедуры +# +# + +# +# Сравнение списков с учетом того что некоторые элементы могут быть +# метками времени, которые проверяются с точностью +-секунда +# Параметр time - список, каждый элемент которого является индексом +# элемента в списке, либо списком индексов во вложенных списках +# +proc listcompare {list1 list2 time} { + foreach e $time { + if {[llength $e]>1} { + lappend a([lindex $e 0]) [lrange $e 1 end] + } else { + set a($e) {} + } + } + if {[llength $list1] !=[llength $list2]} { + return 0 + } + set i 0 + foreach e1 $list1 e2 $list2 { + if {![info exists a($i)]} { + if {[string compare $e1 $e2]!=0} { + return 0 + } + } elseif {[llength $a($i)]} { + if {![listcompare $e1 $e2 $a($i)]} { + return 0 + } + } else { + if {$e2 == "::test::timestamp"} { + set e2 $::test::timestamp + } + if {[regexp {^([[:digit:]]+)\+([[:digit:]]+)$} $e2 m start delta]} { + if {$e1<$start || $e1 >$start+$delta} { + return 0 + } + } elseif {abs($e1-$e2)>1} { + return 0 + } + } + incr i + } + return 1 +} +# Перекодирует строку из кодировки скрипта (assumed 1251) +# в текущую системную + +if {[encoding system] == "utf-8" } { + +proc rus {string} { + return [encoding convertfrom cp1251 $string] +} + +} else { + +proc rus {string} " + return \[encoding convertfrom cp1251 \[encoding convertto [encoding system] \$string\]\] +" + +} + # + # Пишет строку в лог + # + proc log {message} { + variable logchannel + puts $logchannel $message + } + # + # Вызывается при начале теста + # + proc logbegin {testname} { + variable no + variable curtest + incr no + puts -nonewline [rus [format "Тест%5d: %-60s:" $no [string range $testname 0 59]]] + flush stdout + set curtest $testname + log [rus "Тест $no: $testname start"] + } + # + # Вызывается при пропуске теста + # + proc logskip {testname reason} { + variable no + variable p_skip + variable c_skip + puts "[rus [format "Тест%5d: %-60s:" $no [string rang $testname 0 59]]]skipped " + log "[rus "Тест $no: skipped "][expr {$reason=="platform"?"on + the platform $::tcl_platform(platform)":"due to failed prerequisites"}]:[rus $testname]" + incr no + if {$reason == "platform"} { + incr p_skip + } else { + incr c_skip + } + } + + # + # Вызывается при игнорировании теста + # + proc logmiss {testname reason} { + variable no + variable c_skip + puts "[rus [format "Тест%5d: %-60s:" $no [string rang $testname 0 59]]]missed " + log "[rus "Тест $no: missed "][expr {$reason=="platform"?"on + the platform $::tcl_platform(platform)":"by reason: $reason"}]:[rus $testname]" + incr no + incr c_skip + } + + # + # Вызывается конце теста и с параметром ok или failed + # + proc logend {status} { + variable no + variable curtest + variable $status + incr $status + puts $status + log [rus "Тест $no: $curtest ends $status"] + } + + ##################################################################### + # Вспомогательные процедуры, не специфичные для тестируемого + # приложения + ##################################################################### + + # + # Записывает данные из data в файл name. По умолчанию пишет в + # текущей системной кодировке. Можно указать кодировку явно третьим + # аргументом + # + proc makeFile {name data {encoding {}}} { + set f [open $name w] + setFileEncoding $f $encoding + puts -nonewline $f $data + close $f + } + proc setFileEncoding {f encoding} { + if {[string length $encoding]} { + if {"$encoding" == "binary"} { + fconfigure $f -translation binary + } else { + fconfigure $f -encoding $encoding + } + } + } +# +# Возвращает содeржимое файла +# + +proc getFile {filename {encoding {}}} { + set f [open $filename] + setFileEncoding $f $encoding + set data [read $f] + close $f + return $data +} +# +# Возвращает содержимое бинарного файла. Для совместимости со старыми +# тестами +# +proc getfile {filename} { + return [getFile $filename binary] +} + # + # Зачитывает указанный файл, удаляет его и возвращает содержимое. + # По умолчанию читает файл в текущей системной кодировке. Можно + # указать кодировку явно вторым аргументом. + # + + proc readAndDel {name {encoding {}}} { + set f [open $name] + setFileEncoding $f $encoding + set data [read $f] + close $f + file delete $name + return $data + } + + + # + # Защищает файл от записи средствами операционной системы + # denywrite filename ?boolean? + # Если boolean не указан, или он true, файл становится read-only + # Если указан - readwrite (для владельца. Впрочем для не-владельца все + # равно не сработает) + # + proc denyWrite {filename {deny 1}} { + global tcl_platform + if {$tcl_platform(platform) == "unix"} { + set cur_attr [file attributes $filename -permissions] + if {$deny} { + set new_attr [expr {$cur_attr &~ 0200}] + } else { + set new_attr [expr {$cur_attr | 0200}] + } + file attributes $filename -permissions $new_attr + } else { + file attributes $filename -readonly $deny + } + } + # + # Записывает в лог 16-ричный дамп указанной переменной + # + + proc hexdump {data } { + while {[string length $data]} { + set block [string range $data 0 15] + set data [string replace $data 0 15] + binary scan [encoding convertto $block] c* list + set line "" + set i 0 + foreach code $list { + append line [format "%02x " [expr $code>=0?$code:$code +256]] + if {[incr i]%4==0} { + append line "| " + } + } + append line [string repeat " " [expr 56-[string length $line]]] + regsub -all "\[\0-\37\]" $block . printable + append line [rus $printable] + log $line + } + } + namespace export test start_tests end_tests test_log rus log\ + makeFile readAndDel hexdump denyWrite getFile getfile +} +namespace import ::test::* + +package provide test 0.2 diff --git a/tcl_tests/tmpl.try b/tcl_tests/tmpl.try new file mode 100644 index 0000000..731b4ba --- /dev/null +++ b/tcl_tests/tmpl.try @@ -0,0 +1,6 @@ +#!/usr/bin/tclsh +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +start_tests "Тесты на команду dgst" +end_tests diff --git a/tcl_tests/ts.try b/tcl_tests/ts.try new file mode 100644 index 0000000..fe8fa04 --- /dev/null +++ b/tcl_tests/ts.try @@ -0,0 +1,305 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest +cd $::test::dir +set env(LC_TIME) C +if [file exists tsa.serial] { + file delete tsa.serial +} +set testname [file rootname [file tail $::argv0]] + +# +# Formats 64 hex digits into format +# +proc format_hash {hash} { + # Split hash into list of two-digit hexadecimal numbers + set list [regexp -all -inline {[[:xdigit:]]{2}} $hash] + for {set i 0} {$i<[llength $list]} {incr i 16} { + append out [format " %04x - " $i] + set hex [join [lrange $list $i [expr $i+15]] " "] + if {[string length $hex]>24} { + set hex [string replace $hex 23 23 "-"] + } + append out $hex [string repeat " " [expr 50-[string length $hex]]] + foreach char [lrange $list $i [expr $i+15]] { + set n [scan $char "%x"] + if {$n>=32 && $n <=126 } { + append out [format %c $n] + } else { + append out "." + } + } + append out "\n" + } + # Chop newline from last line + return [string trim $out "\n"] +} +# +# Replace Nonce with equvalent number of X-es because we cannot +# predict nonce for comparing output, and chop out STDERR +# If output contains timestamp, extract it, replace with word +# "extracted" and return two-element list where element 0 is modified +# output and element 1 is timestamp in unix time +# +proc cleanup_print {output} { + set data [regsub {STDERR CONTENTS:.*$} [regsub {Nonce: 0x[[:xdigit:]]+} $output {Nonce: 0xXXXXXXXXXXXXXXXX}] {}] + if {[regexp "\nTime stamp: (\[^\n\]+)\n" $data -> time]} { + return [list [regsub "\nTime stamp: (\[^\n\]+)\n" $data "\nTime stamp: extracted\n"] [clock scan $time]] + } else { + return $data + } +} + +start_tests "Тесты на timestamping protocol" + +test "Creating CA 2001" { + makeCA ${testname}CA gost2001:A +} 0 1 + +test "Creating CA 2012" { + makeCA +} 0 1 + +set serial_num 0 + +if {[info exists env(ALG_LIST)]} { + set alg_list $env(ALG_LIST) +} else { + switch -exact [engine_name] { + "ccore" {set alg_list {gost2001:A gost2012_256:A gost2012_512:A}} + "open" {set alg_list {gost2001:A gost2012_256:A gost2012_512:A}} + } +} + +foreach alg $alg_list { + set alg_fn [string map {":" "_"} $alg] + set username U_tsa_$alg_fn + set hash_alg [alg_hash $alg] + switch -glob $alg { + gost2012* {set ::test::ca ${testname}CA-2012} + * {set ::test::ca ${testname}CA} + } + +test "Creating Timestamping authority certificate" { + makeRegisteredUser $username $alg CN "Timestamping authority $alg" \ + extensions "keyUsage=digitalSignature,nonRepudiation\nextendedKeyUsage=critical,timeStamping" + openssl "verify -CAfile $::test::ca/cacert.pem -purpose timestampsign $username/cert.pem" +} 0 "$username/cert.pem: OK\n" + +test "creating TSA configuration file" { + makeFile tsa.cnf "\[ tsa \] +default_tsa = tsa_section +\[ tsa_section \] +serial = [pwd]/tsa.serial +digests=[hash_short_name $hash_alg] +default_policy = 1.2.3.4 +other_policies = 1.2.3.4.5,1.2.3.4.6 +signer_digest = md_gost94 +" +} 0 "" +test "Creating request with [hash_short_name $hash_alg], no cert request, no policy" { + makeFile testdata [string repeat "Test data for timestamp\n" 100] + set hash1 [regsub {^[^=]+= } [openssl "dgst -[hash_short_name $hash_alg] testdata"] {}] + set sha1hash1 [regsub {^[^=]+= } [openssl "dgst -sha1 testdata"] {}] + openssl "ts -query -[hash_short_name $hash_alg] -data testdata -out req1.tsq" + file exists req1.tsq +} 0 1 +test "Printing request 1" { + cleanup_print [openssl "ts -query -text -in req1.tsq"] +} 0 "Version: 1 +Hash Algorithm: [hash_long_name $hash_alg] +Message data: +[format_hash $hash1] +Policy OID: unspecified +Nonce: 0xXXXXXXXXXXXXXXXX +Certificate required: no +Extensions: +" + +test "Creating reply for request1" { + incr serial_num + openssl "ts -reply -config tsa.cnf -queryfile req1.tsq -signer $username/cert.pem -inkey $username/seckey.pem -out resp1.tsr -[hash_short_name $hash_alg]" + file exists resp1.tsr +} 0 1 + set creation_time1 $::test::timestamp +test -time 1 "printing reply 1" { + cleanup_print [openssl "ts -reply -text -in resp1.tsr"] +} 0 [list "Status info: +Status: Granted. +Status description: unspecified +Failure info: unspecified + +TST info: +Version: 1 +Policy OID: 1.2.3.4 +Hash Algorithm: [hash_long_name $hash_alg] +Message data: +[format_hash $hash1] +Serial number: 0x[format "%02X" $serial_num] +Time stamp: extracted +Accuracy: unspecified +Ordering: no +Nonce: 0xXXXXXXXXXXXXXXXX +TSA: unspecified +Extensions: +" $creation_time1] + +test "Verifying reply against query file" { + grep "Verification" [openssl "ts -verify -in resp1.tsr -queryfile req1.tsq -untrusted $username/cert.pem -CAfile $::test::ca/cacert.pem"] +} 0 "Verification: OK\n" + +test "Verifying reply against data file" { + grep "Verification" [openssl "ts -verify -in resp1.tsr -data testdata -untrusted $username/cert.pem -CAfile $::test::ca/cacert.pem"] +} 0 "Verification: OK\n" + + +test "Verifying reply against other data file" { + makeFile testdata2 [string repeat "Test data for timestamp 2\n" 100] + set hash2 [regsub {^[^=]+= } [openssl "dgst -[hash_short_name $hash_alg] testdata2"] {}] + openssl "ts -verify -in resp1.tsr -data testdata2 -untrusted $username/cert.pem -CAfile $::test::ca/cacert.pem" +} 1 "message imprint mismatch" + +test "Verifying reply against explicit hash value" { + grep "Verification" [openssl "ts -verify -in resp1.tsr -digest $hash1 -untrusted $username/cert.pem -CAfile $::test::ca/cacert.pem"] + +} 0 "Verification: OK\n" + +test "Creating request 2 with [hash_short_name $hash_alg], cert requested, no policy" { + openssl "ts -query -[hash_short_name $hash_alg] -data testdata -cert -out req2.tsq" + file exists req2.tsq +} 0 1 + +test "Printing request 2" { + cleanup_print [openssl "ts -query -text -in req2.tsq"] +} 0 "Version: 1 +Hash Algorithm: [hash_long_name $hash_alg] +Message data: +[format_hash $hash1] +Policy OID: unspecified +Nonce: 0xXXXXXXXXXXXXXXXX +Certificate required: yes +Extensions: +" + +test "Replying to request 2" { + incr serial_num + openssl "ts -reply -config tsa.cnf -queryfile req2.tsq -signer $username/cert.pem -inkey $username/seckey.pem -out resp2.tsr -[hash_short_name $hash_alg]" + file exists resp2.tsr +} 0 1 +set creation_time2 $::test::timestamp + +test -time 1 "Printing reply 2" { + cleanup_print [openssl "ts -reply -text -in resp2.tsr"] +} 0 [list "Status info: +Status: Granted. +Status description: unspecified +Failure info: unspecified + +TST info: +Version: 1 +Policy OID: 1.2.3.4 +Hash Algorithm: [hash_long_name $hash_alg] +Message data: +[format_hash $hash1] +Serial number: 0x[format "%02X" $serial_num] +Time stamp: extracted +Accuracy: unspecified +Ordering: no +Nonce: 0xXXXXXXXXXXXXXXXX +TSA: unspecified +Extensions: +" $creation_time2] + +test "Verifying reply2 against request file" { + grep "Verification" [openssl "ts -verify -in resp2.tsr -queryfile req2.tsq -CAfile $::test::ca/cacert.pem"] +} 0 "Verification: OK\n" + +test "Verifying reply2 against data file" { + grep "Verification" [openssl "ts -verify -in resp2.tsr -data testdata -CAfile $::test::ca/cacert.pem"] +} 0 "Verification: OK\n" + +test "Verifying reply2 against explicit digest" { + grep "Verification" [openssl "ts -verify -in resp2.tsr -digest $hash1 -CAfile $::test::ca/cacert.pem"] +} 0 "Verification: OK\n" + +test "Verifying reply2 against request 1" { + grep "Verification" [openssl "ts -verify -in resp2.tsr -queryfile req1.tsq -CAfile $::test::ca/cacert.pem"] +} 1 "nonce mismatch" + +test "Creating request 3 with sha1 digest" { + openssl "ts -query -sha1 -data testdata -cert -out req3.tsq" + file exists req3.tsq +} 0 1 + + +test "Printing request 3" { + cleanup_print [openssl "ts -query -text -in req3.tsq"] +} 0 "Version: 1 +Hash Algorithm: sha1 +Message data: +[format_hash $sha1hash1] +Policy OID: unspecified +Nonce: 0xXXXXXXXXXXXXXXXX +Certificate required: yes +Extensions: +" + +test "Replying to request 3 (with badAlg status)" { + set creation_time3 [clock format [clock seconds] -gmt y -format "%B %d %H:%M:%S %Y GMT"] + openssl "ts -reply -config tsa.cnf -queryfile req3.tsq -signer $username/cert.pem -inkey $username/seckey.pem -out resp3.tsr -[hash_short_name $hash_alg]" + file exists resp3.tsr +} 0 1 + +test "Printing reply 3" { + cleanup_print [openssl "ts -reply -text -in resp3.tsr"] +} 0 "Status info: +Status: Rejected. +Status description: Message digest algorithm is not supported. +Failure info: unrecognized or unsupported algorithm identifier + +TST info: +Not included. +" + +test "Verifying response 3 against request file" { + openssl "ts -verify -in resp3.tsr -queryfile req3.tsq -CAfile $::test::ca/cacert.pem" +} 1 "status code: rejection, status text: Message digest algorithm is not supported., failure codes: badAlg" + + + +test "Creating request 4 with specific (valid) policy" { + openssl "ts -query -[hash_short_name $hash_alg] -data testdata -tspolicy 1.2.3.4.5 -cert -out req4.tsq" + file exists req4.tsq +} 0 1 + +test "Replying to request 4" { + incr serial_num + openssl "ts -reply -config tsa.cnf -queryfile req4.tsq -signer $username/cert.pem -inkey $username/seckey.pem -out resp4.tsr -[hash_short_name $hash_alg]" + file exists resp4.tsr +} 0 1 + +test "Verifying reply 4 against request file" { + grep "Verification" [openssl "ts -verify -in resp4.tsr -queryfile req4.tsq -CAfile $::test::ca/cacert.pem"] +} 0 "Verification: OK\n" + + +test "Creating request 5 with specific (invalid) policy" { + openssl "ts -query -[hash_short_name $hash_alg] -data testdata -tspolicy 1.2.3.4.9 -cert -out req5.tsq" + file exists req5.tsq +} 0 1 + +test "Replying to request 5" { + openssl "ts -reply -config tsa.cnf -queryfile req5.tsq -signer $username/cert.pem -inkey $username/seckey.pem -out resp5.tsr -[hash_short_name $hash_alg]" + file exists resp5.tsr +} 0 1 + + +test "Verifying reply 5" { + openssl "ts -verify -in resp5.tsr -queryfile req5.tsq -CAfile $::test::ca/cacert.pem" +} 1 "status code: rejection, status text: Requested policy is not supported., failure codes: unacceptedPolicy" + + +} + +end_tests diff --git a/tcl_tests/vn4.ciphers b/tcl_tests/vn4.ciphers new file mode 100644 index 0000000..c6cd506 --- /dev/null +++ b/tcl_tests/vn4.ciphers @@ -0,0 +1,15 @@ +gost2001:XA { + GOST2001-GOST89-GOST89 v-vn4-01.vm.cryptocom.ru:443:iis + GOST2012-GOST8912-GOST8912 v-vn4-01.vm.cryptocom.ru:443:iis +# GOST2001-NULL-GOST94 v-vn4-01.vm.cryptocom.ru:443:iis +# GOST2012-NULL-GOST12 v-vn4-01.vm.cryptocom.ru:443:iis +} +gost2012_256:XA { + GOST2012-GOST8912-GOST8912 v-vn4-12S.vm.cryptocom.ru:443:iis +# GOST2012-NULL-GOST12 v-vn4-12S.vm.cryptocom.ru:443:iis +} +gost2012_512:A { + GOST2012-GOST8912-GOST8912 v-vn4-12L.vm.cryptocom.ru:443:iis +# GOST2012-NULL-GOST12 v-vn4-12L.vm.cryptocom.ru:443:iis +} + diff --git a/tcl_tests/wcli.try b/tcl_tests/wcli.try new file mode 100644 index 0000000..35ed578 --- /dev/null +++ b/tcl_tests/wcli.try @@ -0,0 +1,150 @@ +#!/usr/bin/tclsh +# -*- coding: cp1251 -*- +lappend auto_path [file dirname [info script]] +package require ossltest + +#Первый параметр задает используемый сайферсьют. +#Вариант p0 не предназначена для использования в автоматических тестах, так как +#мы не можем программно оценить корректность результата. При использовании +#этого варианта тесты будут гарантированно фейлиться, поэтому использовать +#его следует только для ручного запуска и внимательно читать логи. +array set cipher_name { + p8k GOST2012-KUZNYECHIK-KUZNYECHIKOMAC + p8m GOST2012-MAGMA-MAGMAOMAC + p2 GOST2012-GOST8912-GOST8912 + p1 GOST2001-GOST89-GOST89 + p20 GOST2012-NULL-GOST12 + p10 GOST2001-NULL-GOST94 + p0 {} +} +proc cipher_openssl {sn} {return $::cipher_name($sn)} +proc cipher_command_line_option {sn} { + if {$sn == "p0"} { + return $::cipher_name($sn) + } else { + return "-cipher $::cipher_name($sn)" + } +} + +proc ciphers_usage {} { + global cipher_name + set res {} + foreach name [array names cipher_name] { + append res [format "\t%-3s - %s\n" $name $cipher_name($name)] + } + return $res +} + +# Второй параметр задает четвёрку значений: +#- алгоритм ключа сервера +#- параметры ключа сервера +#- список имен клиентских сертификатов +#- алгоритм ключа УЦ +array set alg_name { + 5xa {gost2012_512 A {Xchg512A Sign512A} gost2012_512} + 2xa {gost2012_256 XA {Xchg256A Sign256A} gost2012_256} + 1xa {gost2001 XA {XchgA SignA} gost2001} +} +proc alg_alg {sn} {return [lindex $::alg_name($sn) 0]} +proc alg_crtdir {sn} {return [format "srv_%s_%s" [lindex $::alg_name($sn) 0] [lindex $::alg_name($sn) 1]]} +proc alg_openssl {sn} {return [format "%s:%s" [lindex $::alg_name($sn) 0] [lindex $::alg_name($sn) 1]]} +proc alg_certid_list {sn} {return [lindex $::alg_name($sn) 2]} +proc alg_ca {sn} {return [lindex $::alg_name($sn) 3]} +proc algs_usage {} { + global alg_name + set res {} + foreach name [array names alg_name] { + append res [format "\t%-3s - %s:%s\n" $name [lindex $alg_name($name) 0] [lindex $alg_name($name) 1]] + } + return $res +} + +if {$argc < 1 || ![regexp {^([^-]+)-([^-]+)-([^-]+)-(.+)$} [lindex $argv 0] -> cipher alg tls host]} { + puts stderr "Usage $argv0 cipher-alg-tlsver-hostname \[s_server-option\]" + puts stderr "cipher:\n[ciphers_usage]" + puts stderr "alg:\n[algs_usage]" + puts stderr "tlsver: -tls* s_server option" + exit 1 +} +set test::suffix "-$cipher-$alg-$tls-$host[lindex $argv 1]" +if {![regexp @ $host]} { + set host build@$host +} + +set CAhost lynx.lan.cryptocom.ru +set CAprefix /cgi-bin/autoca +set mydir [file normalize [file dirname [info script]]] + +cd $::test::dir +set http_tcl http.[info hostname].[clock seconds].[pid].tcl + +start_tests "CSP клиент ($cipher, $alg, $host) [lindex $argv 1]" + +test "Делаем копию http.tcl на $host" { + save_env2 {LD_LIBRARY_PATH OPENSSL_CONF} + catch {unset env(LD_LIBRARY_PATH)} + catch {unset env(OPENSSL_CONF)} + exec $env(CVS_RSH) $host "cat >$http_tcl" < $mydir/http.tcl + restore_env2 {LD_LIBRARY_PATH OPENSSL_CONF} + set copied 1 +} 0 1 + +set crtdir [alg_crtdir $alg] + +test -platformex {![file exists $crtdir/cert.pem]} "Получаем сертификат HTTPS-сервера" { + if {![makeUser $crtdir [alg_openssl $alg] CN [info hostname]]} { + error "Request generation failed" + } + registerUserAtCA $crtdir $CAhost $CAprefix [alg_ca $alg] + file exists $crtdir/cert.pem +} 0 1 + + +test -platformex {![file exists ca_[alg_ca $alg].pem]} "Получаем сертификат CA" { + getCAcert $CAhost $CAprefix [alg_ca $alg] + file exists ca_[alg_ca $alg].pem +} 0 1 + + +custom_client "$env(CVS_RSH) $host tclsh $http_tcl" \ + {LD_LIBRARY_PATH OPENSSL_CONF} + +set server_args [concat [cipher_command_line_option $cipher] \ + [list -bugs -msg -cert $crtdir/cert.pem -key $crtdir/seckey.pem \ + -CAfile ca_[alg_ca $alg].pem -www] -$tls [lindex $argv 1]] + + +test -skip {![info exists copied]} "Сервер не требует сертификата" { + set list [client_server https://[info hostname]:4433 $server_args {}] + grep New, [lindex $list 0] +} 0 "New, TLSv1/SSLv3, Cipher is [cipher_openssl $cipher]\n" + + +test -skip {![info exists copied]} "Сервер требует сертификат, сертификата нет" { + set list [client_server \ + [list https://[info hostname]:4433 no-such-cert-at-all] \ + [concat $server_args {-Verify 2}] {}] + list [lindex $list 2] [lindex [split [lindex $list 1] " "] 0] +} 0 [list 1 "0x80072f0c"] + + +foreach alg_certid [alg_certid_list $alg] { + test -skip {![info exists copied]} \ + "Сервер требует сертификат, клиент $alg_certid" { + set list [client_server \ + [list https://[info hostname]:4433 $alg_certid] \ + [ concat $server_args {-Verify 2}] {}] + grep New, [lindex $list 0] + } 0 "New, TLSv1/SSLv3, Cipher is [cipher_openssl $cipher]\n" +} + +test "Удаляем копию http.tcl на $host" { + save_env2 {LD_LIBRARY_PATH OPENSSL_CONF} + catch {unset env(LD_LIBRARY_PATH)} + catch {unset env(OPENSSL_CONF)} + set rc [exec $env(CVS_RSH) $host rm -f $http_tcl] + restore_env2 {LD_LIBRARY_PATH OPENSSL_CONF} + set rc +} 0 "" + +end_tests diff --git a/tcl_tests/yarrowc.tcl b/tcl_tests/yarrowc.tcl new file mode 100644 index 0000000..66a8153 --- /dev/null +++ b/tcl_tests/yarrowc.tcl @@ -0,0 +1,70 @@ +set argport 7670 +if {[lindex $argv 0] eq "-port"} { + set argport [lindex $argv 1] + set argv [lrange $argv 2 end] +} +set request [lindex $argv 0] +set len [switch $request ping {expr -1} protocol {expr -2} version {expr -3} check {expr 1} default {expr $request}] +set read_data {} + +proc get_port {} { + if {[regexp {^\d+$} $::argport]} {return $::argport} + set f [open $::argport r] + set r [read -nonewline $f] + close $f + return $r +} + +proc get_data {socket} { + set read_data [read $socket] + if {$read_data eq ""} { + close $socket + handle_data + } else { + append ::read_data $read_data + } +} + +proc handle_data {} { + global len read_data + if {$len > 0} { + if {$::request eq "check" && $read_data ne ""} {exit 0} + if {$read_data eq ""} { + puts stderr "not ready" + exit 1 + } + binary scan $read_data H* data + set data [regsub -all ".{48}" [regsub -all ".." $data "& "] "&\n"] + if {[string index $data end] eq "\n"} {set data [string replace $data end end]} + puts $data + } else { + if {$len == -1 || $len == -3} { + if {[string length $read_data] < 4} {error "Not enough data"} + binary scan $read_data I rlen + set read_data [string range $read_data 4 end] + puts [encoding convertfrom utf-8 $read_data] + if {[string length $read_data] != $rlen} { + puts stderr "Real string length [string length $read_data] != claimed $rlen!" + exit 2 + } + } elseif {$len == -2} { + if {[string length $read_data] < 4} {error "Not enough data"} + if {[string length $read_data] > 4} {error "Excess data"} + binary scan $read_data I r + puts $r + } + } + exit 0 +} + +set port [get_port] + +if {[info exists errmsg] && $errmsg ne ""} {error $errmsg} +if {$port eq ""} {error "Cannot find port number"} + +set s [socket localhost $port] +fconfigure $s -encoding binary -buffering none -blocking 0 +fileevent $s readable [list get_data $s] +puts -nonewline $s [binary format I $len] +after 4000 {puts stderr "Timeout. Read for now: '$read_data'"; exit 2} +vwait forever -- 2.39.2