1# -*- mode: cperl; -*-
2
3# Elliptic curve CSR tests
4#
5# This software is copyright (c) 2014 by Gideon Knocke.
6# Copyright (c) 2016 Gideon Knocke, Timothe Litt
7#
8# This is free software; you can redistribute it and/or modify it under
9# the same terms as the Perl 5 programming language system itself.
10#
11# Terms of the Perl programming language system itself
12#
13# a) the GNU General Public License as published by the Free
14#   Software Foundation; either version 1, or (at your option) any
15#   later version, or
16# b) the "Artistic License"
17#
18# See LICENSE for details.
19#
20use strict;
21use warnings;
22
23use Test::More 0.94;
24
25use File::Spec;
26use Crypt::PKCS10;
27
28unless( eval { require Crypt::PK::ECC; } ) {
29    plan skip_all => "Crypt::PK::ECC is not installed, skipping ECC tests";
30}
31
32plan tests => 24;
33
34pass( 'configuration' );
35diag( sprintf( "Perl %s version %vd\n", $^X, $^V ) );
36
37ok( Crypt::PKCS10->setAPIversion(1), 'setAPIversion 1' );
38
39my @dirpath = (File::Spec->splitpath( $0 ))[0,1];
40
41my $decoded;
42$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr4.pem' ),
43                               readFile => 1, escapeStrings => 1, PEMonly => 1 );
44
45isnt( $decoded, undef, 'load PEM from file' ) or BAIL_OUT( Crypt::PKCS10->error );
46
47is( scalar $decoded->subject, '/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd', 'subject' );
48
49is( $decoded->commonName, "", 'CSR commonName' );
50
51is( $decoded->subjectPublicKey, '048d0507a7ebf58a17910fe2b15b0c451e93bc948a4bafb7bf1204d6043e7de1394230befab9c5115cd3cd1e059a545788bb1e0830ee06300c4f3e8d87128f3ddc', 'hex subjectPublicKey' );
52
53is( $decoded->subjectPublicKey(1), << '_KEYPEM_', 'PEM subjectPublicKey' );
54-----BEGIN PUBLIC KEY-----
55MFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABI0FB6fr9YoXkQ/isVsMRR6TvJSK
56S6+3vxIE1gQ+feE5QjC++rnFEVzTzR4FmlRXiLseCDDuBjAMTz6NhxKPPdw=
57-----END PUBLIC KEY-----
58_KEYPEM_
59
60is( $decoded->signature, '30440220730d25ebe5f187c607577cc106d3141dc7f90827914f2a6a11ebc9de6fdf1d26022042c02e4819f2c16c56181205c6c2176902f20cbfcfdc1fa82b30f79bd15d2172',
61    'signature' );
62
63is( scalar $decoded->subject, '/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd',
64    'subject()' );
65
66is( $decoded->pkAlgorithm, 'ecPublicKey', 'encryption algorithm' );
67
68is_deeply( $decoded->subjectPublicKeyParams,
69           {keytype => 'ECC',
70            keylen => 256,
71            curve => 'brainpoolP256r1',
72            'pub_x' => '8D0507A7EBF58A17910FE2B15B0C451E93BC948A4BAFB7BF1204D6043E7DE139',
73            'pub_y' => '4230BEFAB9C5115CD3CD1E059A545788BB1E0830EE06300C4F3E8D87128F3DDC',
74           }, 'subjectPublicKeyParams(EC brainpool)' );
75
76is( $decoded->signatureAlgorithm, 'ecdsa-with-SHA256', 'signature algorithm' );
77
78my $sig = $decoded->signature( 2 );
79ok( defined $sig &&
80    substr( $sig->{r}->as_hex, 2 ) eq '730d25ebe5f187c607577cc106d3141dc7f90827914f2a6a11ebc9de6fdf1d26' &&
81    substr( $sig->{s}->as_hex, 2 ) eq '42c02e4819f2c16c56181205c6c2176902f20cbfcfdc1fa82b30f79bd15d2172',
82    'ECDSA signature components' );
83
84
85my $key = $decoded->subjectPublicKey(1);
86
87isnt( $key = Crypt::PK::ECC->new( \$key ), undef, 'parse EC key' );
88# Seems curve_name can be reported in UPPERcase with old version of
89# Crypt::PK::ECC.  curve_oid can also be missing...
90my $kh = $key->key2hash;
91if( $kh->{curve_name} =~ /^BRAINPOOLP256R1$/ || !exists $kh->{curve_oid}) {
92    BAIL_OUT( "Crypt::PK::ECC version is too old" );
93}
94my $keyh = {
95   'curve_A' => '7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9',
96   'curve_B' => '26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6',
97   'curve_Gx' => '8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262',
98   'curve_Gy' => '547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997',
99   'curve_bits' => 256,
100   'curve_bytes' => 32,
101   'curve_cofactor' => 1,
102   'curve_name' => 'brainpoolp256r1',
103   'curve_oid' => '1.3.36.3.3.2.8.1.1.7',
104   'curve_order' => 'A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7',
105   'curve_prime' => 'A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377',
106   'k' => '',
107   'pub_x' => '8D0507A7EBF58A17910FE2B15B0C451E93BC948A4BAFB7BF1204D6043E7DE139',
108   'pub_y' => '4230BEFAB9C5115CD3CD1E059A545788BB1E0830EE06300C4F3E8D87128F3DDC',
109   'size' => 32,
110   'type' => 0,
111           };
112is_deeply( $kh, $keyh, 'extract EC parameters' );
113
114is_deeply( $decoded->subjectPublicKeyParams(1), {
115   'curve' => 'brainpoolP256r1',
116   'detail' => $keyh,
117   'keylen' => 256,
118   'keytype' => 'ECC',
119   'pub_x' => '8D0507A7EBF58A17910FE2B15B0C451E93BC948A4BAFB7BF1204D6043E7DE139',
120   'pub_y' => '4230BEFAB9C5115CD3CD1E059A545788BB1E0830EE06300C4F3E8D87128F3DDC',
121                                                }, 'detailed EC parameters' );
122
123
124$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr6.pem' ),
125                               readFile => 1, PEMonly => 1, escapeStrings => 1 );
126
127isnt( $decoded, undef, 'load PEM from file' ) or BAIL_OUT( Crypt::PKCS10->error );
128
129is( $decoded->pkAlgorithm, 'ecPublicKey', 'encryption algorithm' );
130
131is_deeply( $decoded->subjectPublicKeyParams,
132           {keytype => 'ECC',
133            keylen => 384,
134            curve => 'secp384r1',
135            'pub_x' => '43FCD15809728171AECA3029A002C13424E92F5D39C3FB7074B5B4B8802FA3E9AB79E1F6CC174596AA09C6BEA9DFAAFF',
136            'pub_y' => '1891F7048842DF14F3FDCABB81C40BDDBFDA64A20FCEA13136DF8109AB56D205F857A295ED00C6B7FAFB6240D66447EB',
137           }, 'subjectPublicKeyParams(EC secp)' );
138
139is( $decoded->signatureAlgorithm, 'ecdsa-with-SHA384', 'signature algorithm' );
140
141$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr7.pem' ),
142                               readFile => 1, PEMonly => 1, escapeStrings => 1 );
143
144is( $decoded, undef, 'bad signature rejected' ) or BAIL_OUT( Crypt::PKCS10->error );
145
146$decoded = Crypt::PKCS10->new( File::Spec->catpath( @dirpath, 'csr7.pem' ),
147                               readFile => 1, PEMonly => 1, escapeStrings => 1, verifySignature => 0 );
148
149isnt( $decoded, undef, 'bad signature loaded' ) or BAIL_OUT( Crypt::PKCS10->error );
150
151ok( !$decoded->checkSignature, 'checkSignature returns false' );
152ok( defined Crypt::PKCS10->error, 'checkSignature sets error string' );
153
154