Coverage Report

Created: 2026-01-25 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/media_tools/crypt_tools.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2024
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Media Tools sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
27
28
#include <gpac/crypt_tools.h>
29
#include <gpac/xml.h>
30
#include <gpac/base_coding.h>
31
#include <gpac/constants.h>
32
#include <gpac/filters.h>
33
#include <gpac/network.h>
34
35
36
#if !defined(GPAC_DISABLE_CRYPTO)
37
38
static u32 cryptinfo_get_crypt_type(char *cr_type)
39
0
{
40
0
  if (!stricmp(cr_type, "ISMA") || !stricmp(cr_type, "iAEC"))
41
0
    return GF_CRYPT_TYPE_ISMA;
42
0
  else if (!stricmp(cr_type, "CENC AES-CTR") || !stricmp(cr_type, "cenc"))
43
0
    return GF_CRYPT_TYPE_CENC;
44
0
  else if (!stricmp(cr_type, "piff"))
45
0
    return GF_CRYPT_TYPE_PIFF;
46
0
  else if (!stricmp(cr_type, "CENC AES-CBC") || !stricmp(cr_type, "cbc1"))
47
0
    return GF_CRYPT_TYPE_CBC1;
48
0
  else if (!stricmp(cr_type, "ADOBE") || !stricmp(cr_type, "adkm"))
49
0
    return GF_CRYPT_TYPE_ADOBE;
50
0
  else if (!stricmp(cr_type, "CENC AES-CTR Pattern") || !stricmp(cr_type, "cens"))
51
0
    return GF_CRYPT_TYPE_CENS;
52
0
  else if (!stricmp(cr_type, "CENC AES-CBC Pattern") || !stricmp(cr_type, "cbcs"))
53
0
    return GF_CRYPT_TYPE_CBCS;
54
0
  else if (!stricmp(cr_type, "OMA"))
55
0
    return GF_ISOM_OMADRM_SCHEME;
56
0
  else if (!stricmp(cr_type, "HLS SAES"))
57
0
    return GF_HLS_SAMPLE_AES_SCHEME;
58
59
0
  GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] Unrecognized crypto type %s\n", cr_type));
60
0
  return 0;
61
0
}
62
63
static void cryptinfo_node_start(void *sax_cbck, const char *node_name, const char *name_space, const GF_XMLAttribute *attributes, u32 nb_attributes)
64
0
{
65
0
  GF_XMLAttribute *att;
66
0
  GF_TrackCryptInfo *tkc;
67
0
  u32 i;
68
0
  GF_CryptInfo *info = (GF_CryptInfo *)sax_cbck;
69
70
0
  if (!strcmp(node_name, "OMATextHeader")) {
71
0
    info->in_text_header = 1;
72
0
    return;
73
0
  }
74
0
  if (!strcmp(node_name, "GPACDRM")) {
75
0
    for (i=0; i<nb_attributes; i++) {
76
0
      att = (GF_XMLAttribute *) &attributes[i];
77
0
      if (!stricmp(att->name, "type")) {
78
0
        info->def_crypt_type = cryptinfo_get_crypt_type(att->value);
79
0
      }
80
0
    }
81
0
    return;
82
0
  }
83
84
85
0
  if (!strcmp(node_name, "CrypTrack")) {
86
0
    Bool has_key = GF_FALSE;
87
0
    Bool has_common_key = GF_TRUE;
88
0
    GF_SAFEALLOC(tkc, GF_TrackCryptInfo);
89
0
    if (!tkc) {
90
0
      GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] Cannnot allocate crypt track, skipping\n"));
91
0
      info->last_parse_error = GF_OUT_OF_MEM;
92
0
      return;
93
0
    }
94
    //by default track is encrypted
95
0
    tkc->IsEncrypted = 1;
96
0
    tkc->sai_saved_box_type = GF_ISOM_BOX_TYPE_SENC;
97
0
    tkc->scheme_type = info->def_crypt_type;
98
99
    //allocate a key to store the default values in single-key mode
100
0
    tkc->keys = gf_malloc(sizeof(GF_CryptKeyInfo));
101
0
    if (!tkc->keys) {
102
0
      GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] Cannnot allocate key IDs\n"));
103
0
      gf_free(tkc);
104
0
      info->last_parse_error = GF_OUT_OF_MEM;
105
0
      return;
106
0
    }
107
0
    memset(tkc->keys, 0, sizeof(GF_CryptKeyInfo));
108
0
    gf_list_add(info->tcis, tkc);
109
110
0
    for (i=0; i<nb_attributes; i++) {
111
0
      att = (GF_XMLAttribute *) &attributes[i];
112
0
      if (!stricmp(att->name, "trackID") || !stricmp(att->name, "ID")) {
113
0
        if (!strcmp(att->value, "*")) info->has_common_key = 1;
114
0
        else {
115
0
          tkc->trackID = atoi(att->value);
116
0
          has_common_key = GF_FALSE;
117
0
        }
118
0
      }
119
0
      else if (!stricmp(att->name, "type")) {
120
0
        tkc->scheme_type = cryptinfo_get_crypt_type(att->value);
121
0
      }
122
0
      else if (!stricmp(att->name, "forceType")) {
123
0
        tkc->force_type = GF_TRUE;
124
0
      }
125
0
      else if (!stricmp(att->name, "key")) {
126
0
        GF_Err e;
127
0
        has_key = GF_TRUE;
128
0
        e = gf_bin128_parse(att->value, tkc->keys[0].key );
129
0
        if (e != GF_OK) {
130
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Cannnot parse key value in CrypTrack\n"));
131
0
          info->last_parse_error = GF_BAD_PARAM;
132
0
          return;
133
0
        }
134
0
      }
135
0
      else if (!stricmp(att->name, "salt")) {
136
0
        u32 len, j;
137
0
        char *sKey = att->value;
138
0
        if (!strnicmp(sKey, "0x", 2)) sKey += 2;
139
0
        len = (u32) strlen(sKey);
140
0
        for (j=0; j<len; j+=2) {
141
0
          char szV[5];
142
0
          u32 v;
143
0
          sprintf(szV, "%c%c", sKey[j], sKey[j+1]);
144
0
          sscanf(szV, "%x", &v);
145
0
          tkc->keys[0].IV[j/2] = v;
146
0
          if (j>=30) break;
147
0
        }
148
0
      }
149
0
      else if (!stricmp(att->name, "kms_URI") || !stricmp(att->name, "rightsIssuerURL")) {
150
0
        if (tkc->KMS_URI) gf_free(tkc->KMS_URI);
151
0
        tkc->KMS_URI = gf_strdup(att->value);
152
0
      }
153
0
      else if (!stricmp(att->name, "scheme_URI") || !stricmp(att->name, "contentID")) {
154
0
        if (tkc->Scheme_URI) gf_free(tkc->Scheme_URI);
155
0
        tkc->Scheme_URI = gf_strdup(att->value);
156
0
      }
157
0
      else if (!stricmp(att->name, "selectiveType")) {
158
0
        if (!stricmp(att->value, "Rap")) tkc->sel_enc_type = GF_CRYPT_SELENC_RAP;
159
0
        else if (!stricmp(att->value, "Non-Rap")) tkc->sel_enc_type = GF_CRYPT_SELENC_NON_RAP;
160
0
        else if (!stricmp(att->value, "Rand")) tkc->sel_enc_type = GF_CRYPT_SELENC_RAND;
161
0
        else if (!strnicmp(att->value, "Rand", 4)) {
162
0
          tkc->sel_enc_type = GF_CRYPT_SELENC_RAND_RANGE;
163
0
          tkc->sel_enc_range = atoi(&att->value[4]);
164
0
        }
165
0
        else if (sscanf(att->value, "%u", &tkc->sel_enc_range)==1) {
166
0
          if (tkc->sel_enc_range==1) tkc->sel_enc_range = 0;
167
0
          else tkc->sel_enc_type = GF_CRYPT_SELENC_RANGE;
168
0
        }
169
0
        else if (!strnicmp(att->value, "Preview", 7)) {
170
0
          tkc->sel_enc_type = GF_CRYPT_SELENC_PREVIEW;
171
0
        }
172
0
        else if (!strnicmp(att->value, "Clear", 5)) {
173
0
          tkc->sel_enc_type = GF_CRYPT_SELENC_CLEAR;
174
0
        }
175
0
        else if (!strnicmp(att->value, "ForceClear", 10)) {
176
0
          char *sep = strchr(att->value, '=');
177
0
          tkc->sel_enc_type = GF_CRYPT_SELENC_CLEAR_FORCED;
178
0
          if (sep) {
179
0
            tkc->sel_enc_range = atoi(sep+1);
180
            //if set to 0, move to no selective encryption
181
0
            if (!tkc->sel_enc_range) tkc->sel_enc_type = GF_CRYPT_SELENC_NONE;
182
0
          }
183
0
        }
184
0
        else if (!stricmp(att->value, "None")) {
185
0
        } else {
186
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Unrecognized selective mode %s, ignoring\n", att->value));
187
0
        }
188
0
      }
189
0
      else if (!stricmp(att->name, "clearStsd")) {
190
0
        if (!strcmp(att->value, "none")) tkc->force_clear_stsd_idx = 0;
191
0
        else if (!strcmp(att->value, "before")) tkc->force_clear_stsd_idx = 1;
192
0
        else if (!strcmp(att->value, "after")) tkc->force_clear_stsd_idx = 2;
193
0
        else {
194
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Unrecognized clear stsd type %s, defaulting to no stsd for clear samples\n", att->value));
195
0
        }
196
0
      }
197
0
      else if (!stricmp(att->name, "Preview")) {
198
0
        tkc->sel_enc_type = GF_CRYPT_SELENC_PREVIEW;
199
0
        sscanf(att->value, "%u", &tkc->sel_enc_range);
200
0
      }
201
0
      else if (!stricmp(att->name, "ipmpType")) {
202
0
        if (!stricmp(att->value, "None")) tkc->ipmp_type = 0;
203
0
        else if (!stricmp(att->value, "IPMP")) tkc->sel_enc_type = 1;
204
0
        else if (!stricmp(att->value, "IPMPX")) tkc->sel_enc_type = 2;
205
0
      }
206
0
      else if (!stricmp(att->name, "ipmpDescriptorID")) tkc->ipmp_desc_id = atoi(att->value);
207
0
      else if (!stricmp(att->name, "encryptionMethod")) {
208
0
        if (!strcmp(att->value, "AES_128_CBC")) tkc->encryption = 1;
209
0
        else if (!strcmp(att->value, "None")) tkc->encryption = 0;
210
0
        else if (!strcmp(att->value, "AES_128_CTR") || !strcmp(att->value, "default")) tkc->encryption = 2;
211
0
        else {
212
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Unrecognized encryption algo %s, ignoring\n", att->value));
213
0
        }
214
0
      }
215
0
      else if (!stricmp(att->name, "transactionID")) {
216
0
        if (strlen(att->value)<=16) strcpy(tkc->TransactionID, att->value);
217
0
      }
218
0
      else if (!stricmp(att->name, "textualHeaders")) {
219
0
      }
220
0
      else if (!stricmp(att->name, "IsEncrypted")) {
221
0
        if (!stricmp(att->value, "1"))
222
0
          tkc->IsEncrypted = 1;
223
0
        else
224
0
          tkc->IsEncrypted = 0;
225
0
      }
226
0
      else if (!stricmp(att->name, "IV_size") && (tkc->scheme_type != GF_CRYPT_TYPE_CBCS)) {
227
0
        tkc->keys[0].IV_size = atoi(att->value);
228
0
      }
229
0
      else if (!stricmp(att->name, "first_IV") && (tkc->scheme_type != GF_CRYPT_TYPE_CBCS)) {
230
0
        char *sKey = att->value;
231
0
        if (!strnicmp(sKey, "0x", 2)) sKey += 2;
232
0
        if ((strlen(sKey) == 16) || (strlen(sKey) == 32)) {
233
0
          u32 j;
234
0
          for (j=0; j<strlen(sKey); j+=2) {
235
0
            u32 v;
236
0
            char szV[5];
237
0
            sprintf(szV, "%c%c", sKey[j], sKey[j+1]);
238
0
            sscanf(szV, "%x", &v);
239
0
            tkc->keys[0].IV[j/2] = v;
240
0
          }
241
0
          if (!tkc->keys[0].IV_size) tkc->keys[0].IV_size = (u8) strlen(sKey) / 2;
242
0
        }
243
0
      }
244
0
      else if (!stricmp(att->name, "saiSavedBox")) {
245
0
        if (!stricmp(att->value, "uuid_psec")) tkc->sai_saved_box_type = GF_ISOM_BOX_UUID_PSEC;
246
0
        else if (!stricmp(att->value, "senc")) tkc->sai_saved_box_type = GF_ISOM_BOX_TYPE_SENC;
247
0
        else {
248
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Unrecognized SAI location %s, ignoring\n", att->value));
249
0
        }
250
0
      }
251
0
      else if (!stricmp(att->name, "keyRoll")) {
252
0
        if (!strncmp(att->value, "idx=", 4))
253
0
          tkc->defaultKeyIdx = atoi(att->value+4);
254
0
        else if (!strncmp(att->value, "roll", 4) || !strncmp(att->value, "samp", 4)) {
255
0
          tkc->roll_type = GF_KEYROLL_SAMPLES;
256
0
          if (att->value[4]=='=') tkc->keyRoll = atoi(att->value+5);
257
0
          if (!tkc->keyRoll) tkc->keyRoll = 1;
258
0
        }
259
0
        else if (!strncmp(att->value, "seg", 3)) {
260
0
          tkc->roll_type = GF_KEYROLL_SEGMENTS;
261
0
          if (att->value[3]=='=') tkc->keyRoll = atoi(att->value+4);
262
0
          if (!tkc->keyRoll) tkc->keyRoll = 1;
263
0
        } else if (!strncmp(att->value, "period", 6)) {
264
0
          tkc->roll_type = GF_KEYROLL_PERIODS;
265
0
          if (att->value[6]=='=') tkc->keyRoll = atoi(att->value+7);
266
0
          if (!tkc->keyRoll) tkc->keyRoll = 1;
267
0
        } else if (!strcmp(att->value, "rap")) {
268
0
          tkc->roll_type = GF_KEYROLL_SAPS;
269
0
          if (att->value[3]=='=') tkc->keyRoll = atoi(att->value+4);
270
0
          if (!tkc->keyRoll) tkc->keyRoll = 1;
271
0
        } else {
272
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Unrecognized roll parameter %s, ignoring\n", att->value));
273
0
        }
274
0
      }
275
0
      else if (!stricmp(att->name, "random")) {
276
0
        if (!strcmp(att->value, "true") || !strcmp(att->value, "1") || !strcmp(att->value, "yes")) {
277
0
          tkc->rand_keys=GF_TRUE;
278
0
        }
279
0
        else if (!strcmp(att->value, "false") || !strcmp(att->value, "0") || !strcmp(att->value, "no")) {
280
0
          tkc->rand_keys=GF_FALSE;
281
0
        }
282
0
        else {
283
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Unrecognized random parameter %s, ignoring\n", att->value));
284
0
        }
285
0
      }
286
0
      else if (!stricmp(att->name, "metadata")) {
287
0
        u32 l = 2 * (u32) strlen(att->value) + 3;
288
0
        tkc->metadata = gf_malloc(sizeof(char) * l);
289
0
        l = gf_base64_encode(att->value, (u32) strlen(att->value), tkc->metadata, l);
290
0
        tkc->metadata[l] = 0;
291
0
      }
292
0
      else if (!stricmp(att->name, "crypt_byte_block")) {
293
0
        tkc->crypt_byte_block = atoi(att->value);
294
0
      }
295
0
      else if (!stricmp(att->name, "skip_byte_block")) {
296
0
        tkc->skip_byte_block = atoi(att->value);
297
0
      }
298
0
      else if (!stricmp(att->name, "clear_bytes")) {
299
0
        tkc->clear_bytes = atoi(att->value);
300
0
      }
301
0
      else if (!stricmp(att->name, "byte_offset")) {
302
0
        tkc->crypt_byte_offset = atoi(att->value);
303
0
      }
304
0
      else if (!stricmp(att->name, "constant_IV_size")
305
0
        || (!stricmp(att->name, "IV_size") && (tkc->scheme_type == GF_CRYPT_TYPE_CBCS))
306
0
      ) {
307
0
        tkc->keys[0].constant_IV_size = atoi(att->value);
308
0
        if ((tkc->keys[0].constant_IV_size != 8) && (tkc->keys[0].constant_IV_size != 16)) {
309
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Constant IV size %d is not 8 or 16\n", att->value));
310
0
          info->last_parse_error = GF_BAD_PARAM;
311
0
        }
312
0
      }
313
0
      else if (!stricmp(att->name, "constant_IV")
314
0
        || (!stricmp(att->name, "first_IV") && (tkc->scheme_type == GF_CRYPT_TYPE_CBCS))
315
0
      ) {
316
0
        char *sKey = att->value;
317
0
        if (!strnicmp(sKey, "0x", 2)) sKey += 2;
318
0
        if ((strlen(sKey) == 16) || (strlen(sKey) == 32)) {
319
0
          u32 j;
320
0
          for (j=0; j<strlen(sKey); j+=2) {
321
0
            u32 v;
322
0
            char szV[5];
323
0
            sprintf(szV, "%c%c", sKey[j], sKey[j+1]);
324
0
            sscanf(szV, "%x", &v);
325
0
            tkc->keys[0].IV[j/2] = v;
326
0
          }
327
0
          if (!tkc->keys[0].constant_IV_size) tkc->keys[0].constant_IV_size = (u8) strlen(sKey) / 2;
328
0
        }
329
0
      }
330
0
      else if (!stricmp(att->name, "encryptSliceHeader")) {
331
0
        tkc->allow_encrypted_slice_header = !strcmp(att->value, "yes") ? GF_TRUE : GF_FALSE;
332
0
      }
333
0
      else if (!stricmp(att->name, "encryptNonVCLs")) {
334
0
        if (!strcmp(att->value, "yes"))
335
0
          tkc->allow_encrypted_nonVCLs = GF_CRYPT_NONVCL_CLEAR_NONE;
336
0
        else if (!strcmp(att->value, "no"))
337
0
          tkc->allow_encrypted_nonVCLs = GF_CRYPT_NONVCL_CLEAR_ALL;
338
0
        else
339
0
          tkc->allow_encrypted_nonVCLs = GF_CRYPT_NONVCL_CLEAR_SEI_AUD;
340
0
      }
341
0
      else if (!stricmp(att->name, "blockAlign")) {
342
0
        if (!strcmp(att->value, "disable")) tkc->block_align = 1;
343
0
        else if (!strcmp(att->value, "always")) tkc->block_align = 2;
344
0
        else tkc->block_align = 0;
345
0
      }
346
0
      else if (!stricmp(att->name, "subsamples")) {
347
0
        char *val = att->value;
348
0
        while (val) {
349
0
          char *sep = strchr(val, ';');
350
0
          if (sep) sep[0] = 0;
351
0
          if (!strncmp(val, "subs=", 5)) {
352
0
            if (tkc->subs_crypt) gf_free(tkc->subs_crypt);
353
0
            tkc->subs_crypt = gf_strdup(val+4);
354
0
          }
355
0
          else if (!strncmp(val, "rand", 4)) {
356
0
            tkc->subs_rand = 2;
357
0
            if (val[4]=='=')
358
0
              tkc->subs_rand = atoi(val+5);
359
0
          }
360
0
          else {
361
0
            GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] unrecognized attribute value %s for `subsamples`, ignoring\n", val));
362
0
          }
363
0
          if (!sep) break;
364
0
          sep[0] = ';';
365
0
          val = sep+1;
366
0
        }
367
0
      }
368
0
      else if (!stricmp(att->name, "multiKey")) {
369
0
        if (!strcmp(att->value, "all") || !strcmp(att->value, "on")) tkc->multi_key = GF_TRUE;
370
0
        else if (!strcmp(att->value, "no")) tkc->multi_key = GF_FALSE;
371
0
        else {
372
0
          char *val = att->value;
373
0
          tkc->multi_key = GF_TRUE;
374
0
          while (val) {
375
0
            char *sep = strchr(val, ';');
376
0
            if (sep) sep[0] = 0;
377
0
            if (!strncmp(val, "roll=", 5)) {
378
0
              tkc->mkey_roll_plus_one = 1 + atoi(val+5);
379
0
            }
380
0
            else if (!strncmp(val, "subs=", 5)) {
381
0
              if (tkc->mkey_subs) gf_free(tkc->mkey_subs);
382
0
              tkc->mkey_subs = gf_strdup(val+5);
383
0
            }
384
0
            else {
385
0
              GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] unrecognized attribute value %s for `multiKey`, ignoring\n", val));
386
0
              tkc->multi_key = GF_FALSE;
387
0
              if (sep) sep[0] = ';';
388
0
              break;
389
0
            }
390
0
            if (!sep) break;
391
0
            sep[0] = ';';
392
0
            val = sep+1;
393
0
          }
394
0
        }
395
0
      }
396
0
    }
397
0
    if (tkc->scheme_type==GF_CRYPT_TYPE_PIFF) {
398
0
      tkc->sai_saved_box_type = GF_ISOM_BOX_UUID_PSEC;
399
0
    }
400
0
    if (has_common_key) info->has_common_key = 1;
401
402
0
    if ((tkc->keys[0].IV_size != 0) && (tkc->keys[0].IV_size != 8) && (tkc->keys[0].IV_size != 16)) {
403
0
      GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] wrong IV size %d for AES-128, using 16\n", (u32) tkc->keys[0].IV_size));
404
0
      tkc->keys[0].IV_size = 16;
405
0
    }
406
407
0
    if ((tkc->scheme_type == GF_CRYPT_TYPE_CENC) || (tkc->scheme_type == GF_CRYPT_TYPE_CBC1)) {
408
0
      if (tkc->crypt_byte_block || tkc->skip_byte_block) {
409
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] Using scheme type %s, crypt_byte_block and skip_byte_block shall be 0\n", gf_4cc_to_str(tkc->scheme_type) ));
410
0
        tkc->crypt_byte_block = tkc->skip_byte_block = 0;
411
0
      }
412
0
    }
413
414
0
    if ((tkc->scheme_type == GF_CRYPT_TYPE_CENC) || (tkc->scheme_type == GF_CRYPT_TYPE_CBC1) || (tkc->scheme_type == GF_CRYPT_TYPE_CENS)) {
415
0
      if (tkc->keys[0].constant_IV_size) {
416
0
        if (!tkc->keys[0].IV_size) {
417
0
          tkc->keys[0].IV_size = tkc->keys[0].constant_IV_size;
418
0
          GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] Using scheme type %s, constant IV shall not be used, using constant IV as first IV\n", gf_4cc_to_str(tkc->scheme_type)));
419
0
          tkc->keys[0].constant_IV_size = 0;
420
0
        } else {
421
0
          tkc->keys[0].constant_IV_size = 0;
422
0
          GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] Using scheme type %s, constant IV shall not be used, ignoring\n", gf_4cc_to_str(tkc->scheme_type)));
423
0
        }
424
0
      }
425
0
    }
426
0
    if (tkc->scheme_type == GF_ISOM_OMADRM_SCHEME) {
427
      /*default to AES 128 in OMA*/
428
0
      tkc->encryption = 2;
429
0
    }
430
431
0
    if (has_key) tkc->nb_keys = 1;
432
0
  }
433
434
0
  if (!strcmp(node_name, "key")) {
435
0
    u32 IV_size, const_IV_size;
436
0
    Bool kas_civ = GF_FALSE;
437
0
    tkc = (GF_TrackCryptInfo *)gf_list_last(info->tcis);
438
0
    if (!tkc) return;
439
    //only realloc for 2nd and more
440
0
    if (tkc->nb_keys) {
441
0
      tkc->keys = (GF_CryptKeyInfo *)gf_realloc(tkc->keys, sizeof(GF_CryptKeyInfo)*(tkc->nb_keys+1));
442
0
      memset(&tkc->keys[tkc->nb_keys], 0, sizeof(GF_CryptKeyInfo));
443
0
    }
444
0
    IV_size = tkc->keys[0].IV_size;
445
0
    const_IV_size = tkc->keys[0].constant_IV_size;
446
447
0
    for (i=0; i<nb_attributes; i++) {
448
0
      att = (GF_XMLAttribute *) &attributes[i];
449
450
0
      if (!stricmp(att->name, "KID")) {
451
0
        GF_Err e = gf_bin128_parse(att->value, tkc->keys[tkc->nb_keys].KID);
452
0
        if (e != GF_OK) {
453
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Cannnot parse KID\n"));
454
0
          info->last_parse_error = GF_BAD_PARAM;
455
0
          return;
456
0
        }
457
0
      }
458
0
      else if (!stricmp(att->name, "value")) {
459
0
        GF_Err e = gf_bin128_parse(att->value, tkc->keys[tkc->nb_keys].key);
460
0
        if (e != GF_OK) {
461
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Cannnot parse key value\n"));
462
0
          info->last_parse_error = GF_BAD_PARAM;
463
0
          return;
464
0
        }
465
0
      }
466
0
      else if (!stricmp(att->name, "hlsInfo")) {
467
0
        if (!strstr(att->value, "URI=\"")) {
468
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[CENC] Missing URI in HLS info %s\n", att->value));
469
0
          info->last_parse_error = GF_BAD_PARAM;
470
0
          return;
471
0
        }
472
0
        if (tkc->keys[tkc->nb_keys].hls_info) gf_free(tkc->keys[tkc->nb_keys].hls_info);
473
0
        tkc->keys[tkc->nb_keys].hls_info = gf_strdup(att->value);
474
0
      }
475
0
      else if (!stricmp(att->name, "IV_size")) {
476
0
        IV_size = atoi(att->value);
477
0
      }
478
0
      else if (!stricmp(att->name, "constant_IV")) {
479
0
        char *sKey = att->value;
480
0
        if (!strnicmp(sKey, "0x", 2)) sKey += 2;
481
0
        if ((strlen(sKey) == 16) || (strlen(sKey) == 32)) {
482
0
          u32 j;
483
0
          for (j=0; j<strlen(sKey); j+=2) {
484
0
            u32 v;
485
0
            char szV[5];
486
0
            sprintf(szV, "%c%c", sKey[j], sKey[j+1]);
487
0
            sscanf(szV, "%x", &v);
488
0
            tkc->keys[tkc->nb_keys].IV[j/2] = v;
489
0
          }
490
0
          const_IV_size = (u8) strlen(sKey) / 2;
491
0
          kas_civ = GF_TRUE;
492
0
        }
493
0
      }
494
0
      else if (!stricmp(att->name, "rep")) {
495
0
        if (tkc->keys[tkc->nb_keys].repID) gf_free(tkc->keys[tkc->nb_keys].repID);
496
0
        tkc->keys[tkc->nb_keys].repID = gf_strdup(att->value);
497
0
      }
498
0
      else if (!stricmp(att->name, "period")) {
499
0
        if (tkc->keys[tkc->nb_keys].periodID) gf_free(tkc->keys[tkc->nb_keys].periodID);
500
0
        tkc->keys[tkc->nb_keys].periodID = gf_strdup(att->value);
501
0
      }
502
0
      else if (!stricmp(att->name, "as")) {
503
0
        tkc->keys[tkc->nb_keys].ASID = atoi(att->value);
504
0
      } else {
505
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[CENC] unrecognized attribute %s for `key`, ignoring\n", att->name));
506
0
      }
507
0
    }
508
0
    tkc->keys[tkc->nb_keys].IV_size = IV_size;
509
0
    tkc->keys[tkc->nb_keys].constant_IV_size = const_IV_size;
510
0
    if (!kas_civ && tkc->nb_keys)
511
0
      memcpy(tkc->keys[tkc->nb_keys].IV, tkc->keys[0].IV, 16);
512
0
    tkc->nb_keys++;
513
0
  }
514
0
}
515
516
static void cryptinfo_node_end(void *sax_cbck, const char *node_name, const char *name_space)
517
0
{
518
0
  GF_CryptInfo *info = (GF_CryptInfo *)sax_cbck;
519
0
  if (!strcmp(node_name, "OMATextHeader")) {
520
0
    info->in_text_header = 0;
521
0
    return;
522
0
  }
523
0
}
524
525
static void cryptinfo_text(void *sax_cbck, const char *text, Bool is_cdata)
526
0
{
527
0
  u32 len, len2;
528
0
  GF_TrackCryptInfo *tkc;
529
0
  GF_CryptInfo *info = (GF_CryptInfo *)sax_cbck;
530
531
0
  if (!info->in_text_header) return;
532
533
0
  tkc = (GF_TrackCryptInfo *) gf_list_last(info->tcis);
534
0
  len = (u32) strlen(text);
535
0
  len2 = tkc->TextualHeaders ? (u32) strlen(tkc->TextualHeaders) : 0;
536
537
0
  tkc->TextualHeaders = gf_realloc(tkc->TextualHeaders, sizeof(char) * (len+len2+1));
538
0
  if (!len2) strcpy(tkc->TextualHeaders, "");
539
0
  strcat(tkc->TextualHeaders, text);
540
0
}
541
542
void gf_crypt_info_del(GF_CryptInfo *info)
543
0
{
544
0
  while (gf_list_count(info->tcis)) {
545
0
    u32 i;
546
0
    GF_TrackCryptInfo *tci = (GF_TrackCryptInfo *)gf_list_last(info->tcis);
547
0
    for (i=0; i<tci->nb_keys; i++) {
548
0
      if (tci->keys[i].hls_info)
549
0
        gf_free(tci->keys[i].hls_info);
550
0
      if (tci->keys[i].repID)
551
0
        gf_free(tci->keys[i].repID);
552
0
      if (tci->keys[i].periodID)
553
0
        gf_free(tci->keys[i].periodID);
554
0
    }
555
0
    if (tci->keys) gf_free(tci->keys);
556
0
    if (tci->metadata) gf_free(tci->metadata);
557
0
    if (tci->KMS_URI) gf_free(tci->KMS_URI);
558
0
    if (tci->Scheme_URI) gf_free(tci->Scheme_URI);
559
0
    if (tci->TextualHeaders) gf_free(tci->TextualHeaders);
560
0
    if (tci->subs_crypt) gf_free(tci->subs_crypt);
561
0
    if (tci->mkey_subs) gf_free(tci->mkey_subs);
562
0
    gf_list_rem_last(info->tcis);
563
0
    gf_free(tci);
564
0
  }
565
0
  gf_list_del(info->tcis);
566
0
  gf_free(info);
567
0
}
568
569
GF_CryptInfo *gf_crypt_info_load(const char *file, GF_Err *out_err)
570
0
{
571
0
  GF_Err e;
572
0
  GF_CryptInfo *info;
573
0
  GF_SAXParser *sax;
574
0
  GF_SAFEALLOC(info, GF_CryptInfo);
575
0
  if (!info) {
576
0
    if (out_err) *out_err = GF_OUT_OF_MEM;
577
0
    return NULL;
578
0
  }
579
0
  info->tcis = gf_list_new();
580
0
  sax = gf_xml_sax_new(cryptinfo_node_start, cryptinfo_node_end, cryptinfo_text, info);
581
0
  e = gf_xml_sax_parse_file(sax, file, NULL);
582
0
  if (e<0) {
583
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[DRM] Failed to parse DRM config file: %s", gf_xml_sax_get_error(sax) ));
584
0
    if (out_err) *out_err = e;
585
0
    gf_crypt_info_del(info);
586
0
    info = NULL;
587
0
  } else if (info->last_parse_error) {
588
0
    if (out_err) *out_err = info->last_parse_error;
589
0
    gf_crypt_info_del(info);
590
0
    info = NULL;
591
0
  } else {
592
0
    if (out_err) *out_err = GF_OK;
593
0
  }
594
0
  gf_xml_sax_del(sax);
595
0
  return info;
596
0
}
597
598
#ifndef GPAC_DISABLE_ISOM
599
600
extern char gf_prog_lf;
601
602
static Bool on_decrypt_event(void *_udta, GF_Event *evt)
603
0
{
604
0
  Double progress;
605
0
  u32 *prev_progress = (u32 *)_udta;
606
0
  if (!_udta) return GF_FALSE;
607
0
  if (evt->type != GF_EVENT_PROGRESS) return GF_FALSE;
608
0
  if (!evt->progress.total) return GF_FALSE;
609
610
0
  progress = (Double) (100*evt->progress.done) / evt->progress.total;
611
0
  if ((u32) progress==*prev_progress)
612
0
    return GF_FALSE;
613
614
0
  *prev_progress = (u32) progress;
615
0
#ifndef GPAC_DISABLE_LOG
616
0
  GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Decrypting: % 2.2f %%%c", progress, gf_prog_lf));
617
#else
618
  fprintf(stderr, "Decrypting: % 2.2f %%%c", progress, gf_prog_lf);
619
#endif
620
0
  return GF_FALSE;
621
0
}
622
623
static GF_Err gf_decrypt_file_ex(GF_ISOFile *mp4, const char *drm_file, const char *dst_file, Double interleave_time, const char *fragment_name, u32 fs_dump_flags)
624
0
{
625
0
  char *szArgs = NULL;
626
0
  char an_arg[100];
627
0
  GF_Filter *src, *dst, *dcrypt;
628
0
  GF_FilterSession *fsess;
629
0
  GF_Err e = GF_OK;
630
0
  u32 progress = (u32) -1;
631
632
0
  fsess = gf_fs_new_defaults(0);
633
0
  if (!fsess) {
634
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Decrypter] Failed to create filter session\n"));
635
0
    return GF_OUT_OF_MEM;
636
0
  }
637
638
  //we use implicit mode, don't set any filter ID
639
640
0
  sprintf(an_arg, "mp4dmx:mov=%p", mp4);
641
0
  gf_dynstrcat(&szArgs, an_arg, NULL);
642
0
  if (fragment_name) {
643
0
    gf_dynstrcat(&szArgs, ":sigfrag:catseg=", NULL);
644
0
    gf_dynstrcat(&szArgs, fragment_name, NULL);
645
0
  }
646
0
  src = gf_fs_load_filter(fsess, szArgs, &e);
647
0
  gf_free(szArgs);
648
0
  szArgs = NULL;
649
650
0
  if (!src) {
651
0
    gf_fs_del(fsess);
652
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Decrypter] Cannot load demux filter for source file\n"));
653
0
    return e;
654
0
  }
655
656
0
  gf_dynstrcat(&szArgs, "cdcrypt", NULL);
657
0
  if (drm_file) {
658
0
    gf_dynstrcat(&szArgs, ":cfile=", NULL);
659
0
    gf_dynstrcat(&szArgs, drm_file, NULL);
660
0
  }
661
0
  dcrypt = gf_fs_load_filter(fsess, szArgs, &e);
662
0
  gf_free(szArgs);
663
0
  szArgs = NULL;
664
0
  if (!dcrypt) {
665
0
    gf_fs_del(fsess);
666
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Decrypter] Cannot load decryptor filter\n"));
667
0
    return e;
668
0
  }
669
670
0
  gf_dynstrcat(&szArgs, "xps_inband=auto", NULL);
671
0
  if (fragment_name) {
672
0
    gf_dynstrcat(&szArgs, ":sseg:noinit:store=frag:refrag:cdur=1000000000", NULL);
673
0
  } else {
674
0
    if (interleave_time) {
675
0
      sprintf(an_arg, ":cdur=%g", interleave_time);
676
0
      gf_dynstrcat(&szArgs, an_arg, NULL);
677
0
    } else {
678
0
      gf_dynstrcat(&szArgs, ":store=flat", NULL);
679
0
    }
680
0
  }
681
682
0
  if (gf_isom_has_keep_utc_times(mp4))
683
0
    gf_dynstrcat(&szArgs, ":keep_utc", NULL);
684
685
0
  dst = gf_fs_load_destination(fsess, dst_file, szArgs, NULL, &e);
686
0
  gf_free(szArgs);
687
0
  szArgs = NULL;
688
689
0
  if (!dst) {
690
0
    gf_fs_del(fsess);
691
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Decrypter] Cannot load destination muxer\n"));
692
0
    return GF_FILTER_NOT_FOUND;
693
0
  }
694
695
0
  if (!gf_sys_is_test_mode()
696
0
#ifndef GPAC_DISABLE_LOG
697
0
    && (gf_log_get_tool_level(GF_LOG_APP)!=GF_LOG_QUIET)
698
0
#endif
699
0
    && !gf_sys_is_quiet()
700
0
  ) {
701
0
    gf_fs_enable_reporting(fsess, GF_TRUE);
702
0
    gf_fs_set_ui_callback(fsess, on_decrypt_event, &progress);
703
0
  }
704
#ifdef GPAC_ENABLE_COVERAGE
705
  else if (gf_sys_is_cov_mode()) {
706
    on_decrypt_event(NULL, NULL);
707
  }
708
#endif //GPAC_ENABLE_COVERAGE
709
710
0
  e = gf_fs_run(fsess);
711
0
  if (e>GF_OK) e = GF_OK;
712
0
  if (!e) e = gf_fs_get_last_connect_error(fsess);
713
0
  if (!e) e = gf_fs_get_last_process_error(fsess);
714
715
0
  if (!e) gf_fs_print_unused_args(fsess, NULL);
716
0
  gf_fs_print_non_connected(fsess);
717
0
  if (fs_dump_flags & 1) gf_fs_print_stats(fsess);
718
0
  if (fs_dump_flags & 2) gf_fs_print_connections(fsess);
719
720
0
  gf_fs_del(fsess);
721
0
  return e;
722
0
}
723
724
GF_EXPORT
725
GF_Err gf_decrypt_fragment(GF_ISOFile *mp4, const char *drm_file, const char *dst_file, const char *fragment_name, u32 fs_dump_flags)
726
0
{
727
0
  return gf_decrypt_file_ex(mp4, drm_file, dst_file, 0, fragment_name, fs_dump_flags);
728
0
}
729
GF_EXPORT
730
GF_Err gf_decrypt_file(GF_ISOFile *mp4, const char *drm_file, const char *dst_file, Double interleave_time, u32 fs_dump_flags)
731
0
{
732
0
  return gf_decrypt_file_ex(mp4, drm_file, dst_file, interleave_time, NULL, fs_dump_flags);
733
0
}
734
static Bool on_crypt_event(void *_udta, GF_Event *evt)
735
0
{
736
0
  Double progress;
737
0
  u32 *prev_progress = (u32 *)_udta;
738
0
  if (!_udta) return GF_FALSE;
739
0
  if (evt->type != GF_EVENT_PROGRESS) return GF_FALSE;
740
0
  if (!evt->progress.total) return GF_FALSE;
741
742
0
  progress = (Double) (100*evt->progress.done) / evt->progress.total;
743
0
  if ((u32) progress==*prev_progress)
744
0
    return GF_FALSE;
745
746
0
  *prev_progress = (u32) progress;
747
0
#ifndef GPAC_DISABLE_LOG
748
0
  GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("Encrypting: % 2.2f %%%c", progress, gf_prog_lf));
749
#else
750
  fprintf(stderr, "Encrypting: % 2.2f %%%c", progress, gf_prog_lf);
751
#endif
752
0
  return GF_FALSE;
753
0
}
754
755
static GF_Err gf_crypt_file_ex(GF_ISOFile *mp4, const char *drm_file, const char *dst_file, Double interleave_time, const char *fragment_name, u32 fs_dump_flags)
756
0
{
757
0
  char *szArgs=NULL;
758
0
  char an_arg[100];
759
0
  char *arg_dst=NULL;
760
0
  u32 progress = (u32) -1;
761
0
  GF_Filter *src, *dst, *cryptf;
762
0
  GF_FilterSession *fsess;
763
0
  GF_Err e = GF_OK;
764
765
0
  fsess = gf_fs_new_defaults(0);
766
0
  if (!fsess) {
767
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Encrypter] Failed to create filter session\n"));
768
0
    return GF_OUT_OF_MEM;
769
0
  }
770
771
0
  sprintf(an_arg, "mp4dmx:mov=%p", mp4);
772
0
  gf_dynstrcat(&szArgs, an_arg, NULL);
773
0
  if (fragment_name) {
774
0
    gf_dynstrcat(&szArgs, ":sigfrag:catseg=", NULL);
775
0
    gf_dynstrcat(&szArgs, fragment_name, NULL);
776
0
  }
777
0
  src = gf_fs_load_filter(fsess, szArgs, &e);
778
779
0
  gf_free(szArgs);
780
0
  szArgs = NULL;
781
782
0
  if (!src) {
783
0
    gf_fs_del(fsess);
784
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Encrypter] Cannot load demux for source file: %s\n", gf_error_to_string(e)));
785
0
    return e;
786
0
  }
787
788
0
  gf_dynstrcat(&szArgs, "cecrypt:FID=1:cfile=", NULL);
789
0
  gf_dynstrcat(&szArgs, drm_file, NULL);
790
0
  cryptf = gf_fs_load_filter(fsess, szArgs, &e);
791
792
0
  gf_free(szArgs);
793
0
  szArgs = NULL;
794
795
0
  if (!cryptf) {
796
0
    gf_fs_del(fsess);
797
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Encrypter] Cannot load encryptor: %s\n", gf_error_to_string(e) ));
798
0
    return e;
799
0
  }
800
801
0
  gf_dynstrcat(&szArgs, "SID=1", NULL);
802
0
  if (fragment_name) {
803
0
    gf_dynstrcat(&szArgs, ":sseg:noinit:store=frag:refrag:cdur=1000000000", NULL);
804
0
  } else {
805
0
    if (interleave_time) {
806
0
      sprintf(an_arg, ":cdur=%g", interleave_time);
807
0
      gf_dynstrcat(&szArgs, an_arg, NULL);
808
0
    } else {
809
0
      gf_dynstrcat(&szArgs, ":store=flat", NULL);
810
0
    }
811
0
  }
812
0
  gf_dynstrcat(&szArgs, ":xps_inband=auto", NULL);
813
814
0
  if (gf_isom_has_keep_utc_times(mp4))
815
0
    gf_dynstrcat(&szArgs, ":keep_utc", NULL);
816
817
0
  arg_dst = gf_url_colon_suffix(dst_file, '=');
818
0
  if (arg_dst) {
819
0
    gf_dynstrcat(&szArgs, arg_dst, NULL);
820
0
    arg_dst[0]=0;
821
0
  }
822
0
  dst = gf_fs_load_destination(fsess, dst_file, szArgs, NULL, &e);
823
824
0
  gf_free(szArgs);
825
0
  szArgs = NULL;
826
0
  if (!dst) {
827
0
    gf_fs_del(fsess);
828
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Encrypter] Cannot load destination muxer\n"));
829
0
    return GF_FILTER_NOT_FOUND;
830
0
  }
831
832
0
  if (!gf_sys_is_test_mode()
833
0
#ifndef GPAC_DISABLE_LOG
834
0
    && (gf_log_get_tool_level(GF_LOG_APP)!=GF_LOG_QUIET)
835
0
#endif
836
0
    && !gf_sys_is_quiet()
837
0
  ) {
838
0
    gf_fs_enable_reporting(fsess, GF_TRUE);
839
0
    gf_fs_set_ui_callback(fsess, on_crypt_event, &progress);
840
0
  }
841
#ifdef GPAC_ENABLE_COVERAGE
842
  else if (gf_sys_is_cov_mode()) {
843
    on_crypt_event(NULL, NULL);
844
  }
845
#endif //GPAC_ENABLE_COVERAGE
846
0
  e = gf_fs_run(fsess);
847
0
  if (e>GF_OK) e = GF_OK;
848
0
  if (!e) e = gf_fs_get_last_connect_error(fsess);
849
0
  if (!e) e = gf_fs_get_last_process_error(fsess);
850
851
0
  if (!e) gf_fs_print_unused_args(fsess, NULL);
852
0
  gf_fs_print_non_connected(fsess);
853
0
  if (fs_dump_flags & 1) gf_fs_print_stats(fsess);
854
0
  if (fs_dump_flags & 2) gf_fs_print_connections(fsess);
855
0
  gf_fs_del(fsess);
856
0
  return e;
857
0
}
858
859
GF_EXPORT
860
GF_Err gf_crypt_fragment(GF_ISOFile *mp4, const char *drm_file, const char *dst_file, const char *fragment_name, u32 fs_dump_flags)
861
0
{
862
0
  return gf_crypt_file_ex(mp4, drm_file, dst_file, 0, fragment_name, fs_dump_flags);
863
0
}
864
865
GF_EXPORT
866
GF_Err gf_crypt_file(GF_ISOFile *mp4, const char *drm_file, const char *dst_file, Double interleave_time, u32 fs_dump_flags)
867
0
{
868
0
  return gf_crypt_file_ex(mp4, drm_file, dst_file, interleave_time, NULL, fs_dump_flags);
869
870
0
}
871
#endif //GPAC_DISABLE_ISOM
872
873
874
#endif /* !defined(GPAC_DISABLE_CRYPTO)*/
875