File Coverage

lib/OMA/Download/DRM/CF.pm
Criterion Covered Total %
statement 52 69 75.3
branch 4 18 22.2
condition 5 13 38.4
subroutine 10 16 62.5
pod 9 9 100.0
total 80 125 64.0


line stmt bran cond sub pod time code
1             package OMA::Download::DRM::CF;
2 1     1   5 use strict;
  1         1  
  1         36  
3 1     1   1291 BEGIN {
4 1     1   11416 use Crypt::Rijndael;
  1         1059  
  1         27  
5             }
6             =head1 NAME
7              
8             OMA::Download::DRM::CF - Perl extension for formatting content objects according to the OMA DRM 1.0 specification
9              
10             =head1 DESCRIPTION
11              
12             Packs & encrypts content objects according to the Open Mobile Alliance Digital Rights Management 1.0 specification
13              
14             =head1 SYNOPSIS
15              
16             use OMA::Download::DRM::CF;
17              
18             =head1 CONSTRUCTOR
19              
20             =head2 new
21            
22             my $cf = OMA::Download::DRM::CF->new(
23            
24             ### Mandatory
25             'key' => '0123456789ABCDEF',
26             'data' => \$data,
27             'content-type' => 'image/gif',
28             'content-uri' => 'cid:image239872@foo.bar',
29             'Rights-Issuer' => 'http://example.com/pics/image239872',
30             'Content-Name' => 'Kilimanjaro Uhuru Peak',
31            
32             ### Optional
33             'Content-Description' => 'Nice image from Kilimanjaro',
34             'Content-Vendor' => 'IT Development Belgium',
35             'Icon-URI' => 'http://example.com/icon.gif',
36             );
37              
38             =cut
39             ### Class constructor ----------------------------------------------------------
40             sub new {
41 1     1 1 16 my ($class, %arg)=@_;
42            
43 1         3 for ('key', 'data', 'content-type', 'content-uri', 'Rights-Issuer', 'Content-Name') {
44 6 50       15 die 'Need '.$_ unless $arg{$_};
45             }
46 1 50       5 die "Key must be 128-bit long" if length($arg{key}) != 16;
47            
48 1   50     184 my $self={
      50        
      50        
      50        
49             'key' => $arg{key},
50             'data' => $arg{data},
51             'content-type' => $arg{'content-type'},
52             'content-uri' => $arg{'content-uri'},
53             headers => {
54             #'Encryption-Method' => $arg{'Encryption-Method'} || 'AES128CBC;padding=RFC2630;plaintextlen='.length(${$arg{data}}),
55             'Encryption-Method' => $arg{'Encryption-Method'} || 'AES128CBC',
56             'Rights-Issuer' => $arg{'Rights-Issuer'},
57             'Content-Name' => $arg{'Content-Name'},
58             'Content-Description' => $arg{'Content-Description'} || '',
59             'Content-Vendor' => $arg{'Content-Vendor'} || '',
60             'Icon-URI' => $arg{'Icon-URI'} || ''
61             },
62             'block-size' => 16,
63             };
64 1         4 $self=bless $self, $class;
65 1         5 $self;
66             }
67              
68              
69              
70             =head1 PROPERTIES
71              
72             =head2 key
73              
74             get or set the 128-bit ASCII encryption key
75              
76             print $cf->key;
77            
78             $cf->key('0123456789ABCDEF');
79              
80             =cut
81             sub key {
82 0     0 1 0 my($self, $val)=@_;
83 0 0 0     0 if(defined $val && length($val) == 16) {
84 0         0 $self->{key} = $val ;
85             }
86 0         0 $self->{key};
87             }
88              
89             =head2 data
90              
91             Get or set the reference to the binary content data
92              
93             print ${$cf->data};
94            
95             $cf->data(\$data);
96              
97             =cut
98             sub data {
99 0     0 1 0 my($self, $val)=@_;
100 0 0       0 $self->{data} = $val if defined $val;
101 0         0 $self->{data};
102             }
103              
104             =head2 content_type
105              
106             Get or set the content MIME type
107              
108             print $cf->content_type;
109            
110             $cf->content_type('image/gif');
111              
112             =cut
113             sub content_type {
114 0     0 1 0 my($self, $val)=@_;
115 0 0       0 $self->{'content-type'} = $val if defined $val;
116 0         0 $self->{'content-type'};
117             }
118              
119             =head2 content_uri
120              
121             Get or set the content URI
122              
123             print $cf->content_uri;
124            
125             $cf->content_type('image12345@example.com');
126              
127             =cut
128             sub content_uri {
129 0     0 1 0 my($self, $val)=@_;
130 0 0       0 $self->{'content_uri'} = $val if defined $val;
131 0         0 $self->{'content_uri'};
132             }
133              
134             =head2 header
135              
136             Get or set a header
137              
138             print $cf->header('Content-Vendor');
139            
140             $cf->header('Content-Vendor', 'My Company');
141              
142             =cut
143             sub header {
144 0     0 1 0 my($self, $key, $val)=@_;
145 0 0       0 $self->{headers}{$key} = $val if defined $val;
146 0 0       0 $self->{headers}{$key} || undef;
147             }
148              
149             =head2 mime
150              
151             Returns the formatted content MIME type
152              
153             print $cf->mime;
154              
155             =cut
156 1     1 1 3 sub mime { 'application/vnd.oma.drm.content' }
157              
158             =head2 extension
159              
160             Returns the formatted content file extension
161              
162             print $cf->extension;
163              
164             =cut
165 0     0 1 0 sub extension { '.dcf' }
166              
167             =head1 METHODS
168              
169             =head2 packit
170              
171             Formats the content object
172              
173             print $cf->packit;
174              
175             =cut
176             sub packit {
177 1     1 1 2 my $self=$_[0];
178 1         2 my $res='';
179            
180 1         3 my $cdat=''; # Encrypted data variable
181 1         8 $self->_crypt($self->{data}, \$cdat); # Crypt data
182            
183             #$self->{headers}{'Encryption-Method'}.=length($cdat); #
184            
185             #my $head=$self->_headers."\r\n"; # Get headers
186 1         3 my $head=$self->_headers; # Get headers
187            
188 1         2 $res.=pack("C", 1); # CF Version Number (1)
189 1         4 $res.=pack("C", length($self->{'content-type'})); # Length of ContentType field
190 1         2 $res.=pack("C", length($self->{'content-uri'})); # Length of ContentURI field
191 1         2 $res.=$self->{'content-type'}; # ContentType field
192 1         3 $res.=$self->{'content-uri'}; # ContentURI field
193 1         4 $res.=_uint2uintvar(length($head)); # Length of the Headers field
194 1         3 $res.=_uint2uintvar(length($cdat)); # Length of Data field
195 1         3 $res.=$head; # Headers
196 1         11 $res.=$cdat; # Encrypted data
197 1         17 return $res;
198             }
199              
200              
201              
202              
203             #--- Support routines ----------------------------------------------------------
204             sub _crypt {
205 1     1   2 my($self,$data,$cdat)=@_;
206 1         14 my $cipher = Crypt::Rijndael->new($self->{'key'}, Crypt::Rijndael::MODE_CBC);
207 1         4 $$cdat = $cipher->encrypt($$data._padding($data, $self->{'block-size'}));
208 1         6 1
209             }
210             sub _padding { # Fill in missed bytes
211 1     1   3 my($data,$blocksize)=@_;
212             ### rfc2630 6.3
213 1         3 my $numpad = $blocksize - (length($$data) % $blocksize);
214 1         152 pack("C", $numpad) x $numpad;
215             }
216             sub _headers {
217 1     1   263 my $self=$_[0];
218 1         3 my $res='';
219 1         2 for (keys %{$self->{headers}}) {
  1         7  
220 6 100       17 if ($self->{headers}{$_}) {
221 3         40 $res.=$_.': '.$self->{headers}{$_}."\r\n";
222             }
223             }
224 1         4 $res;
225             }
226             sub _uint2uintvar {
227             ### Lightweight algorithm implementation
228 2   50 2   6 my $int=$_[0] || return pack("C", 0);
229 2         4 my $lst=0; # We begin with the last octet
230 2         2 my $res='';
231 2         7 while ($int > 0) {
232 3         7 $res=pack("C", ($int & 127) | $lst).$res; # Take 7 LSBits, MSBit is clear if last octet
233 3         4 $int>>=7; # Shift 7 bits right
234 3         7 $lst=128; # Next octets wont be lastes
235             }
236 2         4 $res;
237             }
238              
239              
240             1;
241              
242             __END__