File Coverage

blib/lib/WWW/Suffit/RSA.pm
Criterion Covered Total %
statement 75 81 92.5
branch 14 28 50.0
condition 10 38 26.3
subroutine 11 11 100.0
pod 5 5 100.0
total 115 163 70.5


line stmt bran cond sub pod time code
1             package WWW::Suffit::RSA;
2 1     1   71213 use strict;
  1         10  
  1         30  
3 1     1   659 use utf8;
  1         15  
  1         5  
4              
5             =encoding utf-8
6              
7             =head1 NAME
8              
9             WWW::Suffit::RSA - The RSA encryption and signing subclass
10              
11             =head1 VERSION
12              
13             Version 1.00
14              
15             =head1 SYNOPSIS
16              
17             use WWW::Suffit::RSA;
18              
19             my $rsa = WWW::Suffit::RSA->new;
20              
21             $rsa->keygen(2048);
22             my $private_key = $rsa->private_key;
23             my $public_key = $rsa->public_key;
24              
25             my $b64_cipher_text = $rsa->encrypt("test");
26             my $plain_text = $rsa->decrypt($b64_cipher_text);
27              
28             my $signature = $rsa->sign("Text", 256) or die $rsa->error;
29             $rsa->verify("Text", $signature, 256) or die $rsa->error || "Incorrect signature";
30              
31             =head1 DESCRIPTION
32              
33             The RSA encryption and signing subclass
34              
35             This module based on L
36              
37             =head1 METHODS
38              
39             L inherits all of the properties and methods from L and implements the following new ones.
40              
41             =head2 decrypt
42              
43             my $plain_text = $rsa->decrypt($b64_cipher_text);
44              
45             Decrypt a base64 short "string" to plain text
46              
47             =head2 encrypt
48              
49             my $b64_cipher_text = $rsa->encrypt("test");
50              
51             Encrypt a short "string" using the public key and returns base64 string
52              
53             =head2 error
54              
55             $rsa->error($new_error);
56             my $error = $rsa->error;
57              
58             Sets/gets the error string
59              
60             =head2 keygen
61              
62             $rsa->keygen( $key_size );
63             my $public_key = $rsa->public_key;
64             my $private_key = $rsa->private_key;
65              
66             Create a new private/public key pair (the public exponent is 65537).
67             The argument is the key size, default is 2048
68              
69             =head2 private_key
70              
71             The RSA private key to be used in edcoding an asymmetrically signed data
72              
73             =head2 public_key
74              
75             The RSA public key to be used in decoding an asymmetrically signed data
76              
77             =head2 sign
78              
79             my $signature = $rsa->sign($string, $size);
80              
81             Returns the RSA signature for the given size and string.
82             The L attribute is used as the private key.
83             The result is not yet base64 encoded.
84             This method is provided mostly for the purposes of subclassing.
85              
86             =head2 verify
87              
88             my $bool = $rsa->verify($string, $signature, $size);
89              
90             Returns true if the given RSA size algorithm validates the given string and signature.
91             The L attribute is used as the public key.
92             This method is provided mostly for the purposes of subclassing.
93              
94             =head1 DEPENDENCIES
95              
96             L
97              
98             =head1 HISTORY
99              
100             See C file
101              
102             =head1 TO DO
103              
104             See C file
105              
106             =head1 SEE ALSO
107              
108             L, L
109              
110             =head1 AUTHOR
111              
112             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
113              
114             =head1 COPYRIGHT
115              
116             Copyright (C) 1998-2023 D&D Corporation. All Rights Reserved
117              
118             =head1 LICENSE
119              
120             This program is free software; you can redistribute it and/or
121             modify it under the same terms as Perl itself.
122              
123             See C file and L
124              
125             =cut
126              
127             our $VERSION = "1.00";
128              
129 1     1   585 use Mojo::Base -base;
  1         203356  
  1         8  
130 1     1   256 use Mojo::Util qw/b64_encode b64_decode/;
  1         2  
  1         86  
131 1     1   568 use Crypt::OpenSSL::RSA;
  1         6811  
  1         57  
132              
133             use constant {
134 1         881 KEY_SIZE => 2048,
135             SHA_SIZE => 256,
136             KEY_SIZES_MAP => [512, 1024, 2048, 4096],
137             SHA_SIZES_MAP => [224, 256, 384, 512],
138 1     1   13 };
  1         2  
139              
140             has 'key_size' => KEY_SIZE; # RSA key size
141             has 'sha_size' => SHA_SIZE; # RSA SHA size
142             has 'private_key' => ''; # RSA private key
143             has 'public_key' => ''; # RSA public key
144             has 'error' => ''; # Error string
145              
146             sub keygen {
147 1     1 1 152 my $self = shift;
148 1   50     7 my $key_size = shift || $self->key_size || KEY_SIZE;
149 1         17 $self->error(''); # Flush error string first
150              
151             # Correct key size
152             $key_size = KEY_SIZE
153 1 50       11 unless grep {$_ == $key_size} @{(KEY_SIZES_MAP)};
  4         11  
  1         3  
154              
155 1         6602 my $rsa = Crypt::OpenSSL::RSA->generate_key($key_size);
156 1         63 my $private_key = $rsa->get_private_key_string;
157 1         7 $self->private_key($private_key);
158 1         21 my $public_key = $rsa->get_public_key_string;
159 1         25 $self->public_key($public_key);
160              
161 1         13 return $self;
162             }
163             sub encrypt {
164 1     1 1 790 my ($self, $text) = @_;
165 1         5 $self->error(''); # Flush error string first
166 1 50 0     8 $self->error('The text for encrypting is not specified') && return unless $text;
167              
168             # Get RSA public key
169 1   50     4 my $public_key = $self->public_key // '';
170 1 50 0     8 $self->error('Public key not specified') && return unless length $public_key;
171              
172             # Create RSA object
173 1         3 my $rsa_pub = eval {Crypt::OpenSSL::RSA->new_public_key($public_key)};
  1         8  
174 1 50       647 if ($@) {
175 0         0 chomp $@;
176 0         0 $self->error($@);
177 0         0 return;
178             }
179              
180 1         57 return b64_encode($rsa_pub->encrypt($text), '');
181             }
182             sub decrypt {
183 1     1 1 7 my ($self, $cipher) = @_;
184 1         5 $self->error(''); # Flush error string first
185 1 50 0     9 $self->error('The ciphertext for decryption is not specified') && return unless $cipher;
186              
187             # Get RSA private key
188 1   50     3 my $private_key = $self->private_key // '';
189 1 50 0     8 $self->error('Private key not specified') && return unless length $private_key;
190              
191             # Create RSA object
192 1         33 my $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key);
193              
194 1   50     3 my $plaintext = eval {$rsa_priv->decrypt(b64_decode($cipher))} // '';
  1         282  
195 1 50       6 if ($@) {
196 0         0 chomp $@;
197 0         0 $self->error($@);
198 0         0 return;
199             }
200              
201 1         10 return $plaintext;
202             }
203             sub sign {
204 1     1 1 624 my ($self, $text, $size) = @_;
205 1   50     8 $size ||= $self->sha_size || SHA_SIZE;
      33        
206 1         10 $self->error(''); # Flush error string first
207              
208             # Get RSA private key
209 1   50     7 my $private_key = $self->private_key // '';
210 1 50 0     7 $self->error('Private key not specified') && return unless length $private_key;
211              
212             # Correct sha size
213             $size = SHA_SIZE
214 1 50       2 unless grep {$_ == $size} @{(SHA_SIZES_MAP)};
  4         10  
  1         3  
215              
216             # Create RSA object
217 1         25 my $rsa_priv = Crypt::OpenSSL::RSA->new_private_key($private_key);
218              
219 1         13 my $m = $rsa_priv->can("use_sha${size}_hash");
220 1 50 0     4 $self->error('Unsupported SHA hash size') && return unless $m;
221 1         7 $rsa_priv->$m; # Switch to alg
222              
223             # Sign!
224 1         284 return b64_encode($rsa_priv->sign($text), '');
225             }
226             sub verify {
227 1     1 1 9 my ($self, $text, $signature, $size) = @_;
228 1   50     8 $size ||= $self->sha_size || SHA_SIZE;
      33        
229 1         11 $self->error(''); # Flush error string first
230              
231             # Get RSA public key
232 1   50     7 my $public_key = $self->public_key // '';
233 1 50 0     8 $self->error('Public key not specified') && return unless length $public_key;
234              
235             # Correct sha size
236             $size = SHA_SIZE
237 1 50       3 unless grep {$_ == $size} @{(SHA_SIZES_MAP)};
  4         15  
  1         2  
238              
239             # Create RSA object
240 1         36 my $rsa_pub = Crypt::OpenSSL::RSA->new_public_key($public_key);
241              
242 1         27 my $m = $rsa_pub->can("use_sha${size}_hash");
243 1 50 0     5 $self->error('Unsupported SHA hash size') && return unless $m;
244 1         3 $rsa_pub->$m; # Switch to alg
245              
246             # Verify!
247 1 50       54 return $rsa_pub->verify($text, b64_decode($signature)) ? 1 : 0;
248             }
249              
250             1;
251              
252             __END__