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__ |