You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
13 KiB
344 lines
13 KiB
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// Data objects encapsulating the clear key Ecm (Entitlement Control
|
|
// Message) and related container messages. Deserialization and decryption
|
|
// are handled externally to reduce build-time dependencies.
|
|
//
|
|
// Simplified typical client-side use:
|
|
// Asset asset; // from the AssetRegistry.
|
|
// uint8[] ecm_buffer; // received over network, contains an EcmContainer.
|
|
// EcmContainer ecm_container;
|
|
// util::Status status = ecm_container.Parse(ecm_buffer);
|
|
// status = ecm_container.descriptor(1).ecm().Decrypt(
|
|
// ecm_container.descriptor(1).ecm().buffer(), asset_key);
|
|
// string content_key;
|
|
// if (ecm_container.descriptor(1).ecm().has_content_key()) {
|
|
// content_key = ecm_container.descriptor(1).ecm().content_key();
|
|
// }
|
|
// // use |content_key| to decrypt content.
|
|
//
|
|
// Simplified typical server-side use:
|
|
// EcmContainer container;
|
|
// string encoded_ecm;
|
|
// // Use the ecm_generator API to encode and encrypt an ECM from data fields.
|
|
// util::Status status = ecm_generator::EncodeECM(..., &encoded_ecm);
|
|
// // Use |encoded_ecm| to initialized the Ecm from this library.
|
|
// Ecm ecm;
|
|
// util::Status status = ecm.Parse(encoded_ecm);
|
|
// EcmDescriptor descriptor(crypto_period_id, ecm);
|
|
// status = container.Add(descriptor);
|
|
// string serialized_container;
|
|
// status = container.Marshall(&serialized_container);
|
|
// // now |serialized_container| can be sent to the STB.
|
|
//
|
|
// Due to past overloading of the term "ECM" this library introduces some
|
|
// new terminology.
|
|
//
|
|
// Ecm: the 32-byte message sent from the head end to a packager that contains
|
|
// the asset_id, system_id, and content_key (clear).
|
|
//
|
|
// EcmDescriptor: contains an Ecm and an id (the crypto period id in the case
|
|
// of the BroadcastEncryptor). It contains no encrypted fields.
|
|
//
|
|
// EcmContainer: sent by the server in the video stream using the ECM pid.
|
|
// This contains 1 or 2 EcmDescriptors and a count. It contains no
|
|
// encrypted fields.
|
|
//
|
|
// The first EcmContainer sent by the server has only one EcmDescriptor. After
|
|
// the first crypto period change, an EcmContainer contains 2 EcmDescriptors.
|
|
// One has an odd id and one has an even id. The decrypted content keys from the
|
|
// Ecms in the EcmDescriptors are used by the Mpeg2 parser as odd and even
|
|
// scrambling keys. As the crypto period changes, the oldest EcmDescriptor is
|
|
// dropped from the EcmContainer and the new EcmDescriptor is added.
|
|
//
|
|
// These classes use a simplified protobuf model. For non-repeating fields,
|
|
// - has_foo() indicates whether the field is populated.
|
|
// - the accessor foo() returns either a value or a const reference.
|
|
// - a mutator sets the value. Primitive types and strings use
|
|
// set_foo(value) while for objects mutable_foo() returns a pointer.
|
|
//
|
|
// To prevent null references, objects (like the Asset contained in an Emm)
|
|
// are allocated as members and can be accessed via foo() even if they have
|
|
// not been populated. The caller must call has_foo() to make sure that the
|
|
// object is valid. Calling mutable_foo() to obtain a pointer causes has_foo()
|
|
// to return true.
|
|
//
|
|
// Repeated fields (like the EcmDescriptors contained in an EcmContainer) are
|
|
// handled differently.
|
|
// - foo_size() returns the number of instances.
|
|
// - the accessor foo(index) returns either a value or a const reference to
|
|
// the instance at index. It is illegal to call with |index| >= the value
|
|
// returned by foo_size(). |index| is checked with CHECK.
|
|
// - a mutator to change the value of the instance. Primitive types and
|
|
// strings use set_foo(index, value) while for objects mutable_foo(index)
|
|
// returns a pointer. It is illegal to call with |index| >= the value
|
|
// returned by foo_size(). |index| is checked with CHECK.
|
|
//
|
|
// Accessing a repeated field with an invalid index causes CHECK to fail.
|
|
// Be sure to call EcmContainer::decriptor_size() before calling descriptor()
|
|
// or mutable_descriptor()!
|
|
//
|
|
#ifndef CLEAR_KEY_ECM_H_
|
|
#define CLEAR_KEY_ECM_H_
|
|
|
|
#include <stddef.h>
|
|
#include <string>
|
|
|
|
#include "protos/license_protos.pb.h"
|
|
|
|
#include <media/stagefright/foundation/ABase.h>
|
|
#include <media/stagefright/foundation/ABuffer.h>
|
|
#include <utils/Errors.h>
|
|
|
|
using namespace std;
|
|
|
|
namespace android {
|
|
namespace clearkeycas {
|
|
|
|
// Entitlement Control Message. It contains clear fields. The asset_id
|
|
// and system_id as well as the content_key are clear.
|
|
//
|
|
// This class is not thread-safe.
|
|
class Ecm {
|
|
public:
|
|
// Wire size of ECM.
|
|
static constexpr size_t kSizeBytes = 16 + 16; // clear fields + clear key
|
|
|
|
// Creates an empty ECM which must be initialized via Parse().
|
|
Ecm();
|
|
|
|
~Ecm();
|
|
|
|
// Parses clear fields of Ecm serialized in |buffer_as_binary| and saves
|
|
// a copy of |buffer_as_binary| for a future DecryptEcm call.
|
|
// Returns:
|
|
// - BAD_VALUE if |buffer_as_binary| is too small.
|
|
// - CLEARKEY_STATUS_INVALIDASSETID via ecm_generator::DecodeEcmClearFields if
|
|
// asset_id is 0.
|
|
// - CLEARKEY_STATUS_INVALIDSYSTEMID via ecm_generator::DecodeEcmClearFields if
|
|
// system_id is 0.
|
|
// Postconditions:
|
|
// - |asset_id_| and |system_id_| are populated with non-zero values.
|
|
// - |buffer_| contains a copy of the serialized Ecm.
|
|
status_t Parse(const sp<ABuffer>& buffer_as_binary);
|
|
|
|
// Parses and decrypts Ecm serialized in |buffer_as_binary| using
|
|
// |asset_from_emm|.asset_key().encryption_key(). It is not necessary to call
|
|
// Parse() first.
|
|
// Returns BAD_VALUE if |buffer_as_binary| is too small.
|
|
// Returns CLEARKEY_STATUS_INVALIDASSETID via
|
|
// ecm_generator::DecodeEcmClearFields if asset_id is 0.
|
|
// Returns CLEARKEY_STATUS_INVALIDSYSTEMID via
|
|
// ecm_generator::DecodeEcmClearFields if system_id is 0.
|
|
// Returns CLEARKEY_STATUS_INVALID_PARAMETER if
|
|
// - asset_id in |asset_from_emm| does not match asset_id in serialized Ecm.
|
|
// Preconditions: |asset_from_emm| must contain asset_id and asset_key fields.
|
|
// Postconditions: asset_id() and system_id() are populated with non-zero
|
|
// values, content_key() is populated with the clear content key.
|
|
status_t Decrypt(const sp<ABuffer>& buffer_as_binary,
|
|
const Asset& asset_from_emm);
|
|
|
|
// |buffer_| is a serialized copy of the Ecm used for later decryption or
|
|
// for marshalling.
|
|
inline bool has_buffer() const { return buffer_ != NULL; }
|
|
const sp<ABuffer> buffer() const { return buffer_; }
|
|
inline void set_buffer(const sp<ABuffer>& buffer) {
|
|
buffer_ = ABuffer::CreateAsCopy(buffer->data(), buffer->size());
|
|
}
|
|
|
|
// |content_key| is the clear, encryption/decryption key generated by the server.
|
|
inline bool has_content_key() const { return content_key_ != NULL; }
|
|
inline void set_content_key(const sp<ABuffer>& value) {
|
|
content_key_ = ABuffer::CreateAsCopy(value->data(), value->size());
|
|
}
|
|
inline const sp<ABuffer> content_key() const { return content_key_; }
|
|
|
|
// |asset_id| from the server.
|
|
inline bool has_asset_id() const { return asset_id_set_; }
|
|
inline uint64_t asset_id() const { return asset_id_; }
|
|
inline void set_asset_id(uint64_t value) {
|
|
asset_id_ = value;
|
|
asset_id_set_ = true;
|
|
}
|
|
|
|
// |system_id| from the server.
|
|
inline bool has_system_id() const { return system_id_set_; }
|
|
inline uint32_t system_id() const { return system_id_; }
|
|
inline void set_system_id(uint32_t value) {
|
|
system_id_ = value;
|
|
system_id_set_ = true;
|
|
}
|
|
|
|
private:
|
|
uint64_t asset_id_;
|
|
bool asset_id_set_;
|
|
sp<ABuffer> buffer_;
|
|
sp<ABuffer> content_key_;
|
|
uint32_t system_id_;
|
|
bool system_id_set_;
|
|
};
|
|
|
|
// Contains an Ecm and and Id.
|
|
// This class is not thread-safe.
|
|
class EcmDescriptor {
|
|
public:
|
|
// Wire size of Id field.
|
|
static constexpr size_t kIdSizeBytes = sizeof(uint16_t);
|
|
// Wire size of EcmDescriptor.
|
|
static constexpr size_t kSizeBytes = Ecm::kSizeBytes + kIdSizeBytes;
|
|
|
|
// Client-side ctor. Populate from a buffer with Parse().
|
|
EcmDescriptor();
|
|
|
|
// Server-side ctor.
|
|
// Args:
|
|
// - |id| is the crypto period ID.
|
|
// - |ecm| is an ECM which must have been intialized with Ecm::Parse().
|
|
EcmDescriptor(uint16_t id, const Ecm& ecm);
|
|
|
|
~EcmDescriptor();
|
|
|
|
// Parses EcmDescriptor and its contained Ecm which are serialized in the
|
|
// binary string |buffer_as_binary|.
|
|
// Returns
|
|
// - BAD_VALUE if |buffer_as_binary| is too short to contain a
|
|
// serialized EcmDescriptor.
|
|
// - Errors returned by Ecm::Parse.
|
|
// Postconditions:
|
|
// - id() is populated. Note that 0 is a legal value.
|
|
// - the clear fields of the contained Ecm have been populated.
|
|
status_t Parse(const sp<ABuffer>& buffer_as_binary);
|
|
|
|
// |id| of the contained Ecm. Typically the crypto period id.
|
|
inline bool has_id() const { return id_set_; }
|
|
inline void set_id(uint16_t value) {
|
|
id_ = value;
|
|
id_set_ = true;
|
|
}
|
|
inline uint16_t id() const { return id_; }
|
|
|
|
// The contained |ecm|.
|
|
inline bool has_ecm() const { return ecm_set_; }
|
|
inline Ecm* mutable_ecm() {
|
|
ecm_set_ = true;
|
|
return &ecm_;
|
|
}
|
|
inline const Ecm& ecm() const { return ecm_; }
|
|
|
|
private:
|
|
Ecm ecm_;
|
|
bool ecm_set_;
|
|
uint16_t id_;
|
|
bool id_set_;
|
|
};
|
|
|
|
// Contains a count and 1 or 2 EcmDescriptors. This is included in the video
|
|
// stream by the sender in the ECM pid.
|
|
// This class is not thread-safe.
|
|
class EcmContainer {
|
|
public:
|
|
// Wire size of the count field.
|
|
static constexpr size_t kCountSizeBytes = sizeof(uint16_t);
|
|
// Minimum wire size assuming one EcmDescriptor.
|
|
static constexpr size_t kMinimumSizeBytes =
|
|
EcmDescriptor::kSizeBytes + kCountSizeBytes;
|
|
static constexpr size_t kMinDescriptorCount = 1;
|
|
static constexpr size_t kMaxDescriptorCount = 2;
|
|
|
|
// Creates an empty EcmContainer which must be populated via Parse()
|
|
// (client-side) or Add() (server-side).
|
|
EcmContainer();
|
|
|
|
~EcmContainer();
|
|
|
|
// Adds an EcmDescriptor for server-side applications.
|
|
// If |count_| is 2, |descriptor| replaces the oldest EcmDescriptor.
|
|
//
|
|
// Returns:
|
|
// - INTERNAL if the EcmContainer is in a bad state (count != 0, 1, or 2).
|
|
// Postconditions:
|
|
// - count() is within bounds (1 or 2).
|
|
status_t Add(const EcmDescriptor& descriptor);
|
|
|
|
// Parses EcmContainer and its contained EcmDescriptors which are serialized
|
|
// in |buffer_as_binary|.
|
|
// Returns
|
|
// - BAD_VALUE if |buffer_as_binary| is too short to contain a
|
|
// serialized EcmDescriptor.
|
|
// - ERROR_OUT_OF_RANGE if the count contained in the serialized EcmContainer
|
|
// is not 1 or 2.
|
|
// - Errors returned by EcmDescriptor::Parse.
|
|
// Postconditions:
|
|
// - count() is within bounds (1 or 2) and.
|
|
// - contained EcmDescriptor(s) parsed and populated.
|
|
status_t Parse(const sp<ABuffer>& buffer_as_binary);
|
|
|
|
inline bool has_count() const { return count_set_; }
|
|
// Sets the |count| of contained EcmDecriptors. Illegal values are silently
|
|
// ignored.
|
|
inline void set_count(size_t count) {
|
|
if (!CountLegal(count)) return;
|
|
count_ = count;
|
|
count_set_ = true;
|
|
}
|
|
// Number of contained EcmDecriptors. Only 1 and 2 are legal values.
|
|
inline size_t count() const { return count_; }
|
|
|
|
// Returns the number of allowable descriptors. This is redundant but is
|
|
// provided for protobuf compatibility.
|
|
inline size_t descriptor_size() const { return count_; }
|
|
|
|
// Returns a pointer to the EcmDescriptor at |index| for valid index values,
|
|
// otherwise calls CHECK and aborts. Always call descriptor_size() first!
|
|
inline EcmDescriptor* mutable_descriptor(size_t index) {
|
|
//CHECK(IndexValid(index));
|
|
return &descriptor_[index];
|
|
}
|
|
|
|
// Returns a reference to the EcmDescriptor at |index| for valid index
|
|
// values, otherwise calls CHECK and aborts. Call descriptor_size() first!
|
|
inline const EcmDescriptor& descriptor(size_t index) const {
|
|
//CHECK(IndexValid(index));
|
|
return descriptor_[index];
|
|
}
|
|
|
|
private:
|
|
// Count value must be 1 or 2.
|
|
inline bool CountLegal(size_t count) const {
|
|
return count <= kMaxDescriptorCount && count >= kMinDescriptorCount;
|
|
}
|
|
// Index must be 0 or 1.
|
|
inline bool IndexLegal(size_t index) const {
|
|
return index < kMaxDescriptorCount;
|
|
}
|
|
// |index| is valid for this object: it is legal and < count_.
|
|
inline bool IndexValid(size_t index) const {
|
|
if (!IndexLegal(index)) return false;
|
|
return index < count_;
|
|
}
|
|
size_t count_;
|
|
bool count_set_;
|
|
EcmDescriptor descriptor_[kMaxDescriptorCount];
|
|
|
|
DISALLOW_EVIL_CONSTRUCTORS(EcmContainer);
|
|
};
|
|
|
|
} // namespace clearkeycas
|
|
} // namespace android
|
|
|
|
#endif // CLEAR_KEY_ECM_H_
|