File Coverage

blib/lib/Net/ACME/Crypt.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package Net::ACME::Crypt;
2              
3             #----------------------------------------------------------------------
4             # This module exists because of a desire to do these computations
5             # in environments where a compiler may not be available.
6             # (Otherwise, CryptX would be ideal.)
7             #----------------------------------------------------------------------
8              
9 14     14   42571 use strict;
  14         30  
  14         311  
10 14     14   43 use warnings;
  14         18  
  14         261  
11              
12 14     14   6054 use Digest::SHA ();
  14         34158  
  14         279  
13 14     14   3991 use JSON ();
  14         48272  
  14         219  
14 14     14   2159 use MIME::Base64 ();
  14         2603  
  14         205  
15              
16 14     14   3526 use Net::ACME::Crypt::RSA ();
  0            
  0            
17              
18             *_encode_b64u = \&MIME::Base64::encode_base64url;
19              
20             *get_rsa_public_jwk = \&Net::ACME::Crypt::RSA::get_public_jwk;
21             *get_rsa_jwk_thumbprint = \&Net::ACME::Crypt::RSA::get_jwk_thumbprint;
22              
23             #Based on Crypt::JWT::encode_jwt(), but focused on this particular
24             #protocol’s needs. Note that UTF-8 will probably get mangled in here,
25             #but that’s not a problem since ACME shouldn’t require sending raw UTF-8.
26             sub create_rs256_jwt {
27             my ( %args ) = @_;
28              
29             # key
30             die "JWS: missing 'key'" if !$args{key};
31              
32             my $payload = $args{payload};
33             my $alg = 'RS256';
34              
35             my $header = $args{extra_headers} ? { %{$args{extra_headers}} } : {};
36              
37             # serialize payload
38             $payload = _payload_enc($payload);
39              
40             # encode payload
41             my $b64u_payload = _encode_b64u($payload);
42              
43             # prepare header
44             $header->{alg} = $alg;
45              
46             # encode header
47             my $json_header = _encode_json($header);
48             my $b64u_header = _encode_b64u($json_header);
49              
50             my $signer_cr = Net::ACME::Crypt::RSA->can("sign_$alg");
51              
52             my $b64u_signature = _encode_b64u( $signer_cr->("$b64u_header.$b64u_payload", $args{key}) );
53              
54             return join('.', $b64u_header, $b64u_payload, $b64u_signature);
55             }
56              
57             #----------------------------------------------------------------------
58              
59             sub _encode_json {
60             my ($payload) = @_;
61              
62             #Always do a canonical encode so that we can test more easily.
63             #Note that JWS itself does NOT require this.
64             return JSON->new()->canonical(1)->encode($payload);
65             }
66              
67             #Taken from Crypt::JWT
68             sub _payload_enc {
69             my ($payload) = @_;
70              
71             if (ref($payload) =~ /^(?:HASH|ARRAY)$/) {
72             $payload = _encode_json($payload);
73             }
74             else {
75             utf8::downgrade($payload, 1) or die "JWT: payload cannot contain wide character";
76             }
77              
78             return $payload;
79             }
80              
81             sub _bigint_to_raw {
82             my ($bigint) = @_;
83              
84             my $hex = $bigint->as_hex();
85             $hex =~ s<\A0x><>;
86              
87             #Ensure that we have an even number of hex digits.
88             if (length($hex) % 2) {
89             substr($hex, 0, 0) = '0';
90             }
91              
92             return pack 'H*', $hex;
93             }
94              
95             1;