1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// Data objects encapsulating the clear key Ecm (Entitlement Control
18// Message) and related container messages. Deserialization and decryption
19// are handled externally to reduce build-time dependencies.
20//
21// Simplified typical client-side use:
22//   Asset asset; // from the AssetRegistry.
23//   uint8[] ecm_buffer; // received over network, contains an EcmContainer.
24//   EcmContainer ecm_container;
25//   util::Status status = ecm_container.Parse(ecm_buffer);
26//   status = ecm_container.descriptor(1).ecm().Decrypt(
27//      ecm_container.descriptor(1).ecm().buffer(), asset_key);
28//   string content_key;
29//   if (ecm_container.descriptor(1).ecm().has_content_key()) {
30//     content_key = ecm_container.descriptor(1).ecm().content_key();
31//   }
32//   // use |content_key| to decrypt content.
33//
34// Simplified typical server-side use:
35//   EcmContainer container;
36//   string encoded_ecm;
37//   // Use the ecm_generator API to encode and encrypt an ECM from data fields.
38//   util::Status status = ecm_generator::EncodeECM(..., &encoded_ecm);
39//   // Use |encoded_ecm| to initialized the Ecm from this library.
40//   Ecm ecm;
41//   util::Status status = ecm.Parse(encoded_ecm);
42//   EcmDescriptor descriptor(crypto_period_id, ecm);
43//   status = container.Add(descriptor);
44//   string serialized_container;
45//   status = container.Marshall(&serialized_container);
46//   // now |serialized_container| can be sent to the STB.
47//
48// Due to past overloading of the term "ECM" this library introduces some
49// new terminology.
50//
51// Ecm: the 32-byte message sent from the head end to a packager that contains
52// the asset_id, system_id, and content_key (clear).
53//
54// EcmDescriptor: contains an Ecm and an id (the crypto period id in the case
55// of the BroadcastEncryptor). It contains no encrypted fields.
56//
57// EcmContainer: sent by the server in the video stream using the ECM pid.
58// This contains 1 or 2 EcmDescriptors and a count. It contains no
59// encrypted fields.
60//
61// The first EcmContainer sent by the server has only one EcmDescriptor. After
62// the first crypto period change, an EcmContainer contains 2 EcmDescriptors.
63// One has an odd id and one has an even id. The decrypted content keys from the
64// Ecms in the EcmDescriptors are used by the Mpeg2 parser as odd and even
65// scrambling keys. As the crypto period changes, the oldest EcmDescriptor is
66// dropped from the EcmContainer and the new EcmDescriptor is added.
67//
68// These classes use a simplified protobuf model. For non-repeating fields,
69// - has_foo() indicates whether the field is populated.
70// - the accessor foo() returns either a value or a const reference.
71// - a mutator sets the value.  Primitive types and strings use
72//   set_foo(value) while for objects mutable_foo() returns a pointer.
73//
74// To prevent null references, objects (like the Asset contained in an Emm)
75// are allocated as members and can be accessed via foo() even if they have
76// not been populated. The caller must call has_foo() to make sure that the
77// object is valid. Calling mutable_foo() to obtain a pointer causes has_foo()
78// to return true.
79//
80// Repeated fields (like the EcmDescriptors contained in an EcmContainer) are
81// handled differently.
82// - foo_size() returns the number of instances.
83// - the accessor foo(index) returns either a value or a const reference to
84//   the instance at index. It is illegal to call with |index| >= the value
85//   returned by foo_size(). |index| is checked with CHECK.
86// - a mutator to change the value of the instance.  Primitive types and
87//   strings use set_foo(index, value) while for objects mutable_foo(index)
88//   returns a pointer. It is illegal to call with |index| >= the value
89//   returned by foo_size(). |index| is checked with CHECK.
90//
91// Accessing a repeated field with an invalid index causes CHECK to fail.
92// Be sure to call EcmContainer::decriptor_size() before calling descriptor()
93// or mutable_descriptor()!
94//
95#ifndef CLEAR_KEY_ECM_H_
96#define CLEAR_KEY_ECM_H_
97
98#include <stddef.h>
99#include <string>
100
101#include "protos/license_protos.pb.h"
102
103#include <media/stagefright/foundation/ABase.h>
104#include <media/stagefright/foundation/ABuffer.h>
105#include <utils/Errors.h>
106
107using namespace std;
108
109namespace android {
110namespace clearkeycas {
111
112// Entitlement Control Message. It contains clear fields. The asset_id
113// and system_id as well as the content_key are clear.
114//
115// This class is not thread-safe.
116class Ecm {
117public:
118    // Wire size of ECM.
119    static constexpr size_t kSizeBytes = 16 + 16; // clear fields + clear key
120
121    // Creates an empty ECM which must be initialized via Parse().
122    Ecm();
123
124    ~Ecm();
125
126    // Parses clear fields of Ecm serialized in |buffer_as_binary| and saves
127    // a copy of |buffer_as_binary| for a future DecryptEcm call.
128    // Returns:
129    // - BAD_VALUE if |buffer_as_binary| is too small.
130    // - CLEARKEY_STATUS_INVALIDASSETID via ecm_generator::DecodeEcmClearFields if
131    //   asset_id is 0.
132    // - CLEARKEY_STATUS_INVALIDSYSTEMID via ecm_generator::DecodeEcmClearFields if
133    //   system_id is 0.
134    // Postconditions:
135    // - |asset_id_| and |system_id_| are populated with non-zero values.
136    // - |buffer_| contains a copy of the serialized Ecm.
137    status_t Parse(const sp<ABuffer>& buffer_as_binary);
138
139    // Parses and decrypts Ecm serialized in |buffer_as_binary| using
140    // |asset_from_emm|.asset_key().encryption_key(). It is not necessary to call
141    // Parse() first.
142    // Returns BAD_VALUE if |buffer_as_binary| is too small.
143    // Returns CLEARKEY_STATUS_INVALIDASSETID via
144    //   ecm_generator::DecodeEcmClearFields if asset_id is 0.
145    // Returns CLEARKEY_STATUS_INVALIDSYSTEMID via
146    //   ecm_generator::DecodeEcmClearFields if system_id is 0.
147    // Returns CLEARKEY_STATUS_INVALID_PARAMETER if
148    // - asset_id in |asset_from_emm| does not match asset_id in serialized Ecm.
149    // Preconditions: |asset_from_emm| must contain asset_id and asset_key fields.
150    // Postconditions: asset_id() and system_id() are populated with non-zero
151    // values, content_key() is populated with the clear content key.
152    status_t Decrypt(const sp<ABuffer>& buffer_as_binary,
153            const Asset& asset_from_emm);
154
155    // |buffer_| is a serialized copy of the Ecm used for later decryption or
156    // for marshalling.
157    inline bool has_buffer() const { return buffer_ != NULL; }
158    const sp<ABuffer> buffer() const { return buffer_; }
159    inline void set_buffer(const sp<ABuffer>& buffer) {
160        buffer_ = ABuffer::CreateAsCopy(buffer->data(), buffer->size());
161    }
162
163    // |content_key| is the clear, encryption/decryption key generated by the server.
164    inline bool has_content_key() const { return content_key_ != NULL; }
165    inline void set_content_key(const sp<ABuffer>& value) {
166        content_key_ = ABuffer::CreateAsCopy(value->data(), value->size());
167    }
168    inline const sp<ABuffer> content_key() const { return content_key_; }
169
170    // |asset_id| from the server.
171    inline bool has_asset_id() const { return asset_id_set_; }
172    inline uint64_t asset_id() const { return asset_id_; }
173    inline void set_asset_id(uint64_t value) {
174        asset_id_ = value;
175        asset_id_set_ = true;
176    }
177
178    // |system_id| from the server.
179    inline bool has_system_id() const { return system_id_set_; }
180    inline uint32_t system_id() const { return system_id_; }
181    inline void set_system_id(uint32_t value) {
182        system_id_ = value;
183        system_id_set_ = true;
184    }
185
186private:
187    uint64_t asset_id_;
188    bool asset_id_set_;
189    sp<ABuffer> buffer_;
190    sp<ABuffer> content_key_;
191    uint32_t system_id_;
192    bool system_id_set_;
193};
194
195// Contains an Ecm and and Id.
196// This class is not thread-safe.
197class EcmDescriptor {
198public:
199    // Wire size of Id field.
200    static constexpr size_t kIdSizeBytes = sizeof(uint16_t);
201    // Wire size of EcmDescriptor.
202    static constexpr size_t kSizeBytes = Ecm::kSizeBytes + kIdSizeBytes;
203
204    // Client-side ctor. Populate from a buffer with Parse().
205    EcmDescriptor();
206
207    // Server-side ctor.
208    // Args:
209    // - |id| is the crypto period ID.
210    // - |ecm| is an ECM which must have been intialized with Ecm::Parse().
211    EcmDescriptor(uint16_t id, const Ecm& ecm);
212
213    ~EcmDescriptor();
214
215    // Parses EcmDescriptor and its contained Ecm which are serialized in the
216    // binary string |buffer_as_binary|.
217    // Returns
218    // - BAD_VALUE if |buffer_as_binary| is too short to contain a
219    //   serialized EcmDescriptor.
220    // - Errors returned by Ecm::Parse.
221    // Postconditions:
222    // - id() is populated. Note that 0 is a legal value.
223    // - the clear fields of the contained Ecm have been populated.
224    status_t Parse(const sp<ABuffer>& buffer_as_binary);
225
226    // |id| of the contained Ecm. Typically the crypto period id.
227    inline bool has_id() const { return id_set_; }
228    inline void set_id(uint16_t value) {
229        id_ = value;
230        id_set_ = true;
231    }
232    inline uint16_t id() const { return id_; }
233
234    // The contained |ecm|.
235    inline bool has_ecm() const { return ecm_set_; }
236    inline Ecm* mutable_ecm() {
237        ecm_set_ = true;
238        return &ecm_;
239    }
240    inline const Ecm& ecm() const { return ecm_; }
241
242private:
243    Ecm ecm_;
244    bool ecm_set_;
245    uint16_t id_;
246    bool id_set_;
247};
248
249// Contains a count and 1 or 2 EcmDescriptors. This is included in the video
250// stream by the sender in the ECM pid.
251// This class is not thread-safe.
252class EcmContainer {
253public:
254    // Wire size of the count field.
255    static constexpr size_t kCountSizeBytes = sizeof(uint16_t);
256    // Minimum wire size assuming one EcmDescriptor.
257    static constexpr size_t kMinimumSizeBytes =
258            EcmDescriptor::kSizeBytes + kCountSizeBytes;
259    static constexpr size_t kMinDescriptorCount = 1;
260    static constexpr size_t kMaxDescriptorCount = 2;
261
262    // Creates an empty EcmContainer which must be populated via Parse()
263    // (client-side) or Add() (server-side).
264    EcmContainer();
265
266    ~EcmContainer();
267
268    // Adds an EcmDescriptor for server-side applications.
269    // If |count_| is 2, |descriptor| replaces the oldest EcmDescriptor.
270    //
271    // Returns:
272    // - INTERNAL if the EcmContainer is in a bad state (count != 0, 1, or 2).
273    // Postconditions:
274    // - count() is within bounds (1 or 2).
275    status_t Add(const EcmDescriptor& descriptor);
276
277    // Parses EcmContainer and its contained EcmDescriptors which are serialized
278    // in |buffer_as_binary|.
279    // Returns
280    // - BAD_VALUE if |buffer_as_binary| is too short to contain a
281    //   serialized EcmDescriptor.
282    // - ERROR_OUT_OF_RANGE if the count contained in the serialized EcmContainer
283    //   is not 1 or 2.
284    // - Errors returned by EcmDescriptor::Parse.
285    // Postconditions:
286    // - count() is within bounds (1 or 2) and.
287    // - contained EcmDescriptor(s) parsed and populated.
288    status_t Parse(const sp<ABuffer>& buffer_as_binary);
289
290    inline bool has_count() const { return count_set_; }
291    // Sets the |count| of contained EcmDecriptors. Illegal values are silently
292    // ignored.
293    inline void set_count(size_t count) {
294        if (!CountLegal(count)) return;
295        count_ = count;
296        count_set_ = true;
297    }
298    // Number of contained EcmDecriptors. Only 1 and 2 are legal values.
299    inline size_t count() const { return count_; }
300
301    // Returns the number of allowable descriptors. This is redundant but is
302    // provided for protobuf compatibility.
303    inline size_t descriptor_size() const { return count_; }
304
305    // Returns a pointer to the EcmDescriptor at |index| for valid index values,
306    // otherwise calls CHECK and aborts. Always call descriptor_size() first!
307    inline EcmDescriptor* mutable_descriptor(size_t index) {
308        //CHECK(IndexValid(index));
309        return &descriptor_[index];
310    }
311
312    // Returns a reference to the EcmDescriptor at |index| for valid index
313    // values, otherwise calls CHECK and aborts. Call descriptor_size() first!
314    inline const EcmDescriptor& descriptor(size_t index) const {
315        //CHECK(IndexValid(index));
316        return descriptor_[index];
317    }
318
319private:
320    // Count value must be 1 or 2.
321    inline bool CountLegal(size_t count) const {
322        return count <= kMaxDescriptorCount && count >= kMinDescriptorCount;
323    }
324    // Index must be 0 or 1.
325    inline bool IndexLegal(size_t index) const {
326        return index < kMaxDescriptorCount;
327    }
328    // |index| is valid for this object: it is legal and < count_.
329    inline bool IndexValid(size_t index) const {
330        if (!IndexLegal(index)) return false;
331        return index < count_;
332    }
333    size_t count_;
334    bool count_set_;
335    EcmDescriptor descriptor_[kMaxDescriptorCount];
336
337    DISALLOW_EVIL_CONSTRUCTORS(EcmContainer);
338};
339
340}  // namespace clearkeycas
341}  // namespace android
342
343#endif  // CLEAR_KEY_ECM_H_
344