• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
107  using namespace std;
108  
109  namespace android {
110  namespace 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.
116  class Ecm {
117  public:
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.
has_buffer()157      inline bool has_buffer() const { return buffer_ != NULL; }
buffer()158      const sp<ABuffer> buffer() const { return buffer_; }
set_buffer(const sp<ABuffer> & 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.
has_content_key()164      inline bool has_content_key() const { return content_key_ != NULL; }
set_content_key(const sp<ABuffer> & value)165      inline void set_content_key(const sp<ABuffer>& value) {
166          content_key_ = ABuffer::CreateAsCopy(value->data(), value->size());
167      }
content_key()168      inline const sp<ABuffer> content_key() const { return content_key_; }
169  
170      // |asset_id| from the server.
has_asset_id()171      inline bool has_asset_id() const { return asset_id_set_; }
asset_id()172      inline uint64_t asset_id() const { return asset_id_; }
set_asset_id(uint64_t value)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.
has_system_id()179      inline bool has_system_id() const { return system_id_set_; }
system_id()180      inline uint32_t system_id() const { return system_id_; }
set_system_id(uint32_t value)181      inline void set_system_id(uint32_t value) {
182          system_id_ = value;
183          system_id_set_ = true;
184      }
185  
186  private:
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.
197  class EcmDescriptor {
198  public:
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.
has_id()227      inline bool has_id() const { return id_set_; }
set_id(uint16_t value)228      inline void set_id(uint16_t value) {
229          id_ = value;
230          id_set_ = true;
231      }
id()232      inline uint16_t id() const { return id_; }
233  
234      // The contained |ecm|.
has_ecm()235      inline bool has_ecm() const { return ecm_set_; }
mutable_ecm()236      inline Ecm* mutable_ecm() {
237          ecm_set_ = true;
238          return &ecm_;
239      }
ecm()240      inline const Ecm& ecm() const { return ecm_; }
241  
242  private:
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.
252  class EcmContainer {
253  public:
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  
has_count()290      inline bool has_count() const { return count_set_; }
291      // Sets the |count| of contained EcmDecriptors. Illegal values are silently
292      // ignored.
set_count(size_t count)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.
count()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.
descriptor_size()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!
mutable_descriptor(size_t index)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!
descriptor(size_t index)314      inline const EcmDescriptor& descriptor(size_t index) const {
315          //CHECK(IndexValid(index));
316          return descriptor_[index];
317      }
318  
319  private:
320      // Count value must be 1 or 2.
CountLegal(size_t count)321      inline bool CountLegal(size_t count) const {
322          return count <= kMaxDescriptorCount && count >= kMinDescriptorCount;
323      }
324      // Index must be 0 or 1.
IndexLegal(size_t index)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_.
IndexValid(size_t index)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