File Coverage

blib/lib/VOMS/Lite/PROXY.pm
Criterion Covered Total %
statement 133 168 79.1
branch 48 100 48.0
condition 18 54 33.3
subroutine 10 11 90.9
pod 0 2 0.0
total 209 335 62.3


line stmt bran cond sub pod time code
1             package VOMS::Lite::PROXY;
2              
3 1     1   1682 use 5.004;
  1         5  
  1         47  
4 1     1   7 use strict;
  1         3  
  1         44  
5 1     1   6 use VOMS::Lite::PEMHelper qw(readCert readAC readPrivateKey);
  1         3  
  1         75  
6 1     1   6 use VOMS::Lite::CertKeyHelper qw(digestSign);
  1         2  
  1         71  
7 1     1   6 use VOMS::Lite::ASN1Helper qw(ASN1Wrap ASN1Unwrap DecToHex Hex ASN1BitStr);
  1         3  
  1         76  
8 1     1   6 use VOMS::Lite::KEY;
  1         2  
  1         34  
9 1     1   6 use VOMS::Lite::X509;
  1         2  
  1         29  
10 1     1   5 use VOMS::Lite::RSAKey;
  1         2  
  1         46  
11              
12             require Exporter;
13 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         3  
  1         2650  
14             @ISA = qw(Exporter);
15              
16             $VERSION = '0.20';
17              
18             sub Examine {
19 0     0 0 0 return VOMS::Lite::X509::Examine(@_);
20             }
21              
22             sub Create {
23              
24             # Load in Context
25 1     1 0 390 my %context = %{ shift() };
  1         6  
26              
27             # Create error and warning arrays
28 1         3 my @Errors;
29             my @Warnings;
30              
31             # Get request time;
32 1         3 my $now=time();
33              
34             # Check for required input values
35 1 50       6 if ( ! defined $context{'Cert'} ) { push @Errors, "PROXY: Issuer certificate not supplied"; }
  0         0  
36 1 50       4 if ( ! defined $context{'Key'} ) { push @Errors, "PROXY: Issuer key not supplied"; }
  0         0  
37              
38             # Bail if there isn't enough information
39 1 50       4 if ( @Errors > 0 ) { return { Errors => \@Errors} ; }
  0         0  
40              
41             # Load input data into local variables
42 1 50       15 my $CertInfoRef = (($context{'Cert'} =~ /^(\060.+)$/s) ? VOMS::Lite::X509::Examine($&, {X509serial=>"", X509subject=>"", End=>""}) : undef);
43 1 50       20 my $KeyInfoRef = (($context{'Key'} =~ /^(\060.+)$/s) ? VOMS::Lite::KEY::Examine($&, {Keymodulus=>"", KeyprivateExponent=>""}) : undef);
44 1 50       3 my %CI; if ( defined $CertInfoRef ) { %CI=%$CertInfoRef; } else { push @Errors, "PROXY: Unable to parse certificate."; }
  1         15  
  1         6  
  0         0  
45 1 50       13 my %KI; if ( defined $KeyInfoRef ) { %KI=%$KeyInfoRef; } else { push @Errors, "PROXY: Unable to parse key."; }
  1         4  
  1         4  
  0         0  
46              
47             # Bail if there is a certificate Parse error
48 1 50       4 if ( @Errors > 0 ) { return { Errors => \@Errors} ; }
  0         0  
49              
50             # Load optional values
51 1 50 33     12 my $type = ((( defined $context{'Type'} && $context{'Type'} =~ /^(Lega[cs]y|Limited|Pre-RFC|RFC)$/) ) ? $& : undef);
52 1 50       4 if ( $type eq "Legasy" ) { $type = "Legacy"; $context{'Type'}="Legacy"; } # oops was going through a bad spell!
  0         0  
  0         0  
53 1 50 33     6 my $pathlen = ((( defined $context{'PathLength'} && $context{'PathLength'} =~ /^([0-9]+)$/s) ) ? $& : undef);
54 1 50 33     4 my $bits = ((( defined $context{'Bits'} && $context{'Bits'} =~ /^(512|1024|2048|4096)$/s) ) ? $& : undef);
55 1 50 33     11 my $lifetime = ((( defined $context{'Lifetime'} && $context{'Lifetime'} =~ /^([0-9]+)$/s) ) ? $& : undef);
56 1 50 33     6 my $start = ((( defined $context{'Start'} && $context{'Start'} =~ /^([0-9]+)$/s) ) ? $& : undef);
57 1 50 33     5 my $AC = ((( defined $context{'AC'} && $context{'AC'} =~ /^(\060.+)$/s) ) ? $& : undef);
58 1         2 my @Ext;
59 1 50       4 if ( defined $context{'Ext'} ) {
60 0 0       0 if ( ref ($context{'Ext'}) eq "ARRAY" ) { @Ext = @{ $context{'Ext'} }; }
  0         0  
  0         0  
61 0         0 else { push @Errors,"PROXY: Ext must be an array reference"; }
62             };
63 1 0       3 foreach (@Ext) { if ( ! /^(\060.+)$/ ) { push @Errors,"Extension ".Hex($1)." isn't DER encoded"; } }
  0         0  
  0         0  
64 1 50 33     5 my $KeypublicE = ((( defined $context{'KeypublicExponent'} && $context{'KeypublicExponent'} =~ /^([\x00-\x7f].+)$/s) ) ? $& : undef);
65 1 50 33     5 my $KeypublicM = ((( defined $context{'KeypublicModulus'} && $context{'KeypublicModulus'} =~ /^([\x00-\x7f].+)$/s) ) ? $& : undef);
66              
67             # Check for unrecognised values for recognised options
68 1 50 33     6 if ( defined $context{'Type'} && ! defined $type ) { push @Errors, "PROXY: Unknown proxy type $context{'Type'}. Try Legacy, Limited, Pre-RFC or RFC."; }
  0         0  
69 1 50 33     33 if ( defined $context{'PathLength'} && ! defined $pathlen ) { push @Errors, "PROXY: Invalid Pathlength $context{'PathLength'}. Must be a positive integer."; }
  0         0  
70 1 50 33     3 if ( defined $context{'Bits'} && ! defined $bits ) { push @Errors, "PROXY: Key size may only be 512, 1024, 2048 or 4096."; }
  0         0  
71 1 50 33     7 if ( defined $context{'Lifetime'} && ! defined $lifetime ) { push @Errors, "PROXY: Invalid Lifetime $context{'Lifetime'}. Must be a positive integer."; }
  0         0  
72 1 50 33     4 if ( defined $context{'Start'} && ! defined $start ) { push @Errors, "PROXY: Invalid Start $context{'Start'}. Must be a positive integer (seconds since epoch)."; }
  0         0  
73 1 50 33     5 if ( defined $context{'AC'} && ! defined $AC ) { push @Errors, "PROXY: AC Must be in DER format."; }
  0         0  
74              
75             # Check for unknown options
76 1 50       2 foreach (keys %context) { if ( ! /^(Quiet|Type|PathLength|Lifetime|AC|Ext|Cert|Key|Start|Bits|KeypublicExponent|KeypublicModulus)$/ ) { push @Errors, "PROXY: $_ is an invalid option.";}}
  4         15  
  0         0  
77              
78 1 50       4 if ( defined $start ) { $now = $start;}
  0         0  
79              
80             # Bail if any recognised options are invalid
81 1 50       4 if ( @Errors > 0 ) { return { Errors => \@Errors} ; }
  0         0  
82              
83             # Warn if there is something queer
84 1 50       4 if ( ! defined $type ) { $type = "Legacy"; push @Warnings, "PROXY: Undefined proxy type. Defaulting to Legacy."; }
  0         0  
  0         0  
85 1 50       3 if ( ! defined $lifetime ) { $lifetime = 43200; push @Warnings, "PROXY: Undefined lifetime. Defaulting to $lifetime seconds."; }
  0         0  
  0         0  
86 1 50       3 if ( ! defined $bits ) { $bits = 512; push @Warnings, "PROXY: Undefined key size. Defaulting to $bits bits."; }
  1         3  
  1         3  
87 1 50       4 if ( $lifetime > 86400 ) { push @Warnings, "PROXY: Requested lifetime exceeds 24 hours."; }
  0         0  
88 1 50       3 if ( ( $lifetime ) > ( $CI{'End'} - $now ) ) { push @Warnings, "PROXY: Requested lifetime exceeds lifetime of issuer."; }
  0         0  
89 1 50       4 if ( ( $CI{'End'} - $now ) < 604800 ) { push @Warnings, "PROXY: Issuer certificate will expire in less than 1 week."; }
  1         2  
90 1 50 33     8 if ( $type =~ "Legacy" && defined $pathlen ) { push @Warnings, "PROXY: Legacy Proxy may not a proxy pathlength."; }
  0         0  
91              
92             ###################################################################
93             # Do not edit below these lines (unless there's a bug of course!) #
94             ###################################################################
95              
96             #Get times Now and Now + $lifetime (12 hours)
97 1         6 my @NOW=gmtime($now );
98 1         4 my @FUT=gmtime($now + $lifetime );
99              
100             # UTCTIME (so two digit years, OK for the next 40 or so years!)
101 1         10 my $beforeDate=sprintf("%02i%02i%02i%02i%02i%02iZ",($NOW[5] % 100),($NOW[4]+1),$NOW[3],$NOW[2],$NOW[1],$NOW[0]);
102 1         5 my $afterDate=sprintf("%02i%02i%02i%02i%02i%02iZ",($FUT[5] % 100),($FUT[4]+1),$FUT[3],$FUT[2],$FUT[1],$FUT[0]);
103              
104 1         1 my ( $Keyversion, $Keymodulus, $KeypublicExponent, $KeyprivateExponent, $Keyprime1, $Keyprime2, $Keyexponent1, $Keyexponent2, $Keycoefficient, $Privatekey);
105              
106 1 50 33     5 if ( ! defined($KeypublicE) || ! defined($KeypublicM) ) {
107              
108             # Generate Key Pair
109 1 50       10 my $keyref = VOMS::Lite::RSAKey::Create( { Bits => $bits, Verbose => (defined $context{'Quiet'})?undef:"y" } );
110 1 50       8 if ( ! defined $keyref ) { return { Errors => [ "PROXY: Key Generation Failure" ] } ; }
  0         0  
111 1         3 my %key = %{ $keyref };
  1         10  
112 1 50       7 if ( defined $key{'Errors'} ) { return { Errors => [ "PROXY: Error in Key Generation ".$key{'Errors'} ] } ; }
  0         0  
113              
114             ### Proxy Private Key#####################################################
115             # Keyversion Keymodulus KeypublicExponent KeyprivateExponent
116             # Keyprime1 Keyprime2 Keyexponent1 Keyexponent2 Keycoefficient
117 1         3 $Keyversion = "020100";
118 1         9 $Keymodulus = ASN1Wrap("02",DecToHex($key{Modulus}));
119 1         7 $KeypublicExponent = ASN1Wrap("02",DecToHex($key{PublicExponent}));
120 1         6 $KeyprivateExponent = ASN1Wrap("02",DecToHex($key{PrivateExponent}));
121 1         8 $Keyprime1 = ASN1Wrap("02",DecToHex($key{Prime1}));
122 1         9 $Keyprime2 = ASN1Wrap("02",DecToHex($key{Prime2}));
123 1         5 $Keyexponent1 = ASN1Wrap("02",DecToHex($key{Exponent1}));
124 1         7 $Keyexponent2 = ASN1Wrap("02",DecToHex($key{Exponent2}));
125 1         8 $Keycoefficient = ASN1Wrap("02",DecToHex($key{Iqmp}));
126              
127 1         10 $Privatekey=ASN1Wrap("30",$Keyversion.$Keymodulus.$KeypublicExponent.$KeyprivateExponent.
128             $Keyprime1.$Keyprime2.$Keyexponent1.$Keyexponent2.$Keycoefficient);
129             } else {
130 0         0 $Keymodulus = ASN1Wrap("02",Hex($KeypublicM));
131 0         0 $KeypublicExponent = ASN1Wrap("02",Hex($KeypublicE));
132             }
133              
134             ###Proxy Public Bits######################################################
135             # TBSCertificate:
136             # X509version X509serial X509signature X509issuer X509validity X509subject
137             # X509subjectPublicKeyInfo (X509issuerUniqueID) (X509subjectUniqueID) X509extensions
138              
139             #Certificate Version (x509 v3)
140 1         4 my $X509version = "a003020102";
141              
142             #Serial Number (different algorithm for (Pre)?RFC and Legacy Globus
143 1         9 my $SN=DecToHex( ((($CI{End}-$now) & hex("00ffffff"))<<8 ) + int(rand 256));
144 1 50       9 my $X509serial=($type eq "Legacy")?Hex($CI{X509serial}):ASN1Wrap("02",$SN);
145              
146             #Use MD5 and RSA for now
147 1         3 my $X509signature="300d06092a864886f70d0101040500"; #SEQ(OID:md5WithRSAEncryption NULL)
148              
149             #Issuer (straight from certificate)
150 1         5 my $X509issuer=Hex($CI{X509subject});
151              
152             #Validity
153 1         6 my $X509Validity=ASN1Wrap("30",ASN1Wrap("17",Hex($beforeDate)).ASN1Wrap("17",Hex($afterDate)));
154              
155             #Subject
156 1         7 my $proxystr = Hex("proxy");
157 1 50       5 $proxystr = Hex("limited proxy") if ($type eq "Limited");
158 1 50 33     29 $proxystr = Hex(hex($SN)) if ($type ne "Legacy" && $type ne "Limited");
159 1         5 my $PROXYNAME=ASN1Wrap("31",ASN1Wrap("30","0603550403".ASN1Wrap("13",$proxystr))); #SET{SEQ{OID:CN CN}}
160 1         8 my $X509subject=ASN1Wrap("30",Hex(scalar ASN1Unwrap($CI{X509subject})).$PROXYNAME);
161              
162             #Public Key
163 1         7 my $PubKeyChunck=ASN1Wrap("30",$Keymodulus.$KeypublicExponent);
164 1         5 my $X509subjectPublicKeyInfo=ASN1Wrap("30",ASN1Wrap("30","06092a864886f70d0101010500").ASN1Wrap("03",ASN1BitStr($PubKeyChunck)));
165              
166             #Extensions
167             #KeyUsage
168 1         5 my $keyusage=ASN1Wrap("30","0603551d0f"."0101ff"."0404030203a8");#Critical:Dig sign & Key encypher & Key Agree
169             ######Proxyinfo not quite right yet
170             #ProxyInfo Extensions SEQ{OID(GlobusProxy|id-ppl-inheritALL) . Criticality . PolicyLangOID+Policy}
171 1 50       5 my $PolicyVal=( defined $pathlen )?ASN1Wrap("a1",ASN1Wrap("02",DecToHex($pathlen))):"";
172 1         3 my $Policy;
173 1 50       5 $Policy=ASN1Wrap("04",ASN1Wrap("30","300a06082b06010505071501".$PolicyVal)) if ($type eq "Pre-RFC");
174 1 50       4 $Policy=ASN1Wrap("04",ASN1Wrap("30",$PolicyVal."300a06082b06010505071501")) if ($type eq "RFC");
175 1         1 my $ProxyInfo="";
176 1 50       3 $ProxyInfo=ASN1Wrap("30","060a2b060104019b5001815e"."0101ff".$Policy) if ($type eq "Pre-RFC");
177 1 50       4 $ProxyInfo=ASN1Wrap("30","06082b0601050507010e". "0101ff".$Policy) if ($type eq "RFC");
178             #VOMS
179 1         2 my $VOMS="";
180             # $VOMS=ASN1Wrap("30","060a2b06010401be45646405"."".ASN1Wrap("04",Hex($AC))) if ( defined $AC );
181 1 50       5 $VOMS=ASN1Wrap("30","060a2b06010401be45646405"."".ASN1Wrap("04",ASN1Wrap("30",ASN1Wrap("30",Hex($AC))))) if ( defined $AC );
182              
183 1         7 my $X509extensions=ASN1Wrap("a3",ASN1Wrap("30",$keyusage.$ProxyInfo.$VOMS.Hex(join('',@Ext))));
184              
185             #The whole chunck of certificate to be signed
186 1         9 my $TBSCertificate=ASN1Wrap("30",$X509version.$X509serial.$X509signature.$X509issuer.$X509Validity.
187             $X509subject.$X509subjectPublicKeyInfo.$X509extensions);
188              
189             ###Signature Bits#####################################################
190             # X509signatureAlgorithm X509signature
191              
192             # Make MD5 Checksum and RSA sign it
193 1         3 my $BinaryTBSCertificate = $TBSCertificate;
194 1         6 $BinaryTBSCertificate =~ s/(..)/pack('C',hex($&))/ge;
  309         743  
195 1         7 my $RSAsignedDigest = digestSign("md5WithRSA",$BinaryTBSCertificate,Hex($KI{KeyprivateExponent}),Hex($KI{Keymodulus}));
196 1         10 my $Signature = ASN1Wrap("03",ASN1BitStr($RSAsignedDigest)); #(Always n*8 bits for MDnRSA and SHA1RSA)
197              
198              
199             ###Wrap it all up Public Bits and Signature############################
200             # TBSCertificate X509signatureAlgorithm X509signature
201              
202 1         10 my $Certificate = ASN1Wrap("30",$TBSCertificate.$X509signature.$Signature);
203              
204             ###Write out the proxy to the proxy file###############################
205             # ProxyCert ProxyKey SigningCerts ##### Would like to put full chain in here!
206              
207 1         10 $Certificate=~s/(..)/pack('C',hex($&))/ge;
  395         950  
208 1 50 33     11 if ( ! defined($KeypublicE) || ! defined($KeypublicM) ) { $Privatekey=~s/(..)/pack('C',hex($&))/ge; }
  1         6  
  318         1066  
209              
210 1         40 return { ProxyCert=>$Certificate, ProxyKey=>$Privatekey, Warnings=>\@Warnings };
211             }
212              
213             1;
214              
215             __END__