├── .gitignore ├── cdmi-stub ├── jsmn │ ├── Makefile │ ├── LICENSE │ ├── jsmn.h │ ├── example │ │ ├── simple.c │ │ └── jsondump.c │ ├── README.md │ ├── jsmn.c │ └── jsmn_test.c ├── keypairs.h ├── Makefile ├── json_web_key.h ├── mediakeys.cpp ├── mediaenginesession.cpp ├── imp.h ├── mediakeysession.cpp └── json_web_key.cpp ├── config_clearkey.mk ├── LICENSE ├── rpc ├── opencdm_callback.x ├── gen │ ├── opencdm_callback_xdr.c │ ├── opencdm_callback_clnt.c │ ├── opencdm_callback.h │ ├── opencdm_callback_svc.c │ ├── opencdm_xdr_clnt.c │ ├── opencdm_xdr_xdr.c │ ├── opencdm_xdr_svc.c │ └── opencdm_xdr.h └── opencdm_xdr.x ├── Makefile ├── libs └── shmemsem │ ├── shmemsem_helper.h │ └── shmemsem_helper.cpp ├── README.md ├── cdmi.h └── service.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | pr/ 2 | service 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Compiled Dynamic libraries 11 | *.so 12 | *.dylib 13 | *.dll 14 | 15 | # Compiled Static libraries 16 | *.lai 17 | *.la 18 | *.a 19 | *.lib 20 | 21 | # Executables 22 | *.exe 23 | *.out 24 | *.app 25 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/Makefile: -------------------------------------------------------------------------------- 1 | # You can put your build options here 2 | -include config.mk 3 | 4 | all: libjsmn.a 5 | 6 | libjsmn.a: jsmn.o 7 | $(AR) rc $@ $^ 8 | 9 | %.o: %.c jsmn.h 10 | $(CC) -c $(CFLAGS) $< -o $@ 11 | 12 | test: jsmn_test 13 | ./jsmn_test 14 | 15 | jsmn_test: jsmn_test.o 16 | $(CC) $(LDFLAGS) -L. -ljsmn $< -o $@ 17 | 18 | jsmn_test.o: jsmn_test.c libjsmn.a 19 | 20 | simple_example: example/simple.o libjsmn.a 21 | $(CC) $(LDFLAGS) $^ -o $@ 22 | 23 | jsondump: example/jsondump.o libjsmn.a 24 | $(CC) $(LDFLAGS) $^ -o $@ 25 | 26 | clean: 27 | rm -f jsmn.o jsmn_test.o example/simple.o 28 | rm -f jsmn_test 29 | rm -f jsmn_test.exe 30 | rm -f libjsmn.a 31 | rm -f simple_example 32 | rm -f jsondump 33 | 34 | .PHONY: all clean test 35 | 36 | -------------------------------------------------------------------------------- /cdmi-stub/keypairs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Linaro Ltd 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 | #ifndef OPENCDM_CDMI_STUB_KEYPAIRS_H 17 | #define OPENCDM_CDMI_STUB_KEYPAIRS_H 18 | 19 | #include 20 | #include 21 | 22 | namespace media { 23 | typedef std::pair KeyIdAndKeyPair; 24 | typedef std::vector KeyIdAndKeyPairs; 25 | } 26 | #endif 27 | -------------------------------------------------------------------------------- /config_clearkey.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Linaro Ltd 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 | # Library dependencies of CDMI module 18 | 19 | CDMI_LFLAGSS:= -lcdmi \ 20 | -ljsmn\ 21 | -lcrypto \ 22 | -Lcdmi-stub \ 23 | -Lcdmi-stub/jsmn 24 | 25 | CDMI_DIR := cdmi-stub 26 | 27 | CDMILIB_NAME = cdmi 28 | 29 | cdmi: 30 | $(MAKE) -C $(CDMI_DIR) 31 | 32 | clean_cdmi: 33 | $(MAKE) -C $(CDMI_DIR) clean 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Fraunhofer FOKUS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /cdmi-stub/jsmn/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Serge A. Zaitsev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /cdmi-stub/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2015 Linaro Ltd 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 | CXXFLAGS = -g 18 | 19 | CDMI_SOURCES = mediakeysession.cpp mediakeys.cpp mediaenginesession.cpp json_web_key.cpp 20 | CDMI_OBJECTS = $(CDMI_SOURCES:.cpp=.o) 21 | CDMI_INCLUDES = -I ../ -I ./jsmn/ 22 | CDMILIB_NAME = cdmi 23 | CDMI_LIBS = -ljsmn -L ./jsmn/ -lcrypto 24 | 25 | .PHONY: clean all jsmn 26 | 27 | all: cdmi 28 | 29 | cdmi: jsmn cdmi_lib 30 | 31 | .cpp.o: 32 | $(CXX) $(CXXFLAGS) $(CDMI_INCLUDES) $(CDMI_LIBS) -c $< -o $@ 33 | 34 | cdmi_lib: $(CDMI_OBJECTS) 35 | $(AR) rcs lib$(CDMILIB_NAME).a $^ 36 | 37 | jsmn: 38 | $(MAKE) -C jsmn/ 39 | 40 | jsmn_clean: 41 | $(MAKE) -C jsmn/ clean 42 | 43 | clean: jsmn_clean 44 | rm -f $(CDMI_OBJECTS) 45 | rm -f lib$(CDMILIB_NAME).a 46 | 47 | -------------------------------------------------------------------------------- /rpc/opencdm_callback.x: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * OpenCDM XDR to be used for RPC communication between DRM and CDM platform counterpart 16 | * Based on EME methods and naming. 17 | */ 18 | 19 | struct rpc_cb_message { 20 | char session_id <>; 21 | string message <>; 22 | string destination_url <>; 23 | }; 24 | 25 | struct rpc_cb_key_status_update { 26 | char session_id <>; 27 | string message <>; 28 | }; 29 | 30 | 31 | struct rpc_cb_ready { 32 | char session_id <>; 33 | }; 34 | 35 | 36 | struct rpc_cb_error { 37 | char session_id <>; 38 | int error; 39 | string error_message <>; 40 | }; 41 | 42 | program OPEN_CDM_CALLBACK { 43 | version OPEN_CDM_EME_5 { 44 | void ON_MESSAGE(rpc_cb_message) = 1; 45 | void ON_READY(rpc_cb_ready) = 2; 46 | void ON_ERROR(rpc_cb_error) = 3; 47 | void ON_KEY_STATUS_UPDATE(rpc_cb_key_status_update) = 4; 48 | } = 1; 49 | } = 0x66666666; 50 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2017 Fraunhofer FOKUS 3 | # 4 | # Licensed under the MIT License (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | # 13 | 14 | 15 | CXXFLAGS = -g 16 | 17 | RPC_DIR = rpc 18 | 19 | RPC_SOURCES = \ 20 | $(RPC_DIR)/gen/opencdm_xdr_xdr.c\ 21 | $(RPC_DIR)/gen/opencdm_xdr_svc.c \ 22 | $(RPC_DIR)/gen/opencdm_callback_xdr.c \ 23 | $(RPC_DIR)/gen/opencdm_callback_clnt.c 24 | 25 | RPC_OBJECTS = $(RPC_SOURCES:.c=.o) 26 | RPC_INCLUDES = -I $(RPC_DIR)/gen 27 | 28 | SERVICE_SOURCES = service.cpp libs/shmemsem/shmemsem_helper.cpp 29 | SERVICE_INCLUDES = $(RPC_INCLUDES) 30 | SERVICE_LIBS = -lrt $(JSMN_LIBS) $ 31 | 32 | .PHONY: clean cdmiservice 33 | 34 | all: cdmi cdmiservice 35 | 36 | .c.o: 37 | $(CXX) $(CXXFLAGS) $(RPC_INCLUDES) -c $< -o $@ 38 | 39 | .cpp.o: 40 | $(CXX) $(CXXFLAGS) $(CDMI_INCLUDES) -c $< -o $@ 41 | 42 | 43 | rpc: $(RPC_OBJECTS) 44 | 45 | cdmiservice: $(RPC_OBJECTS) 46 | $(CXX) $(CXXFLAGS) -pthread -std=c++0x \ 47 | $(CDMI_INCLUDES) $(SERVICE_INCLUDES) -L . \ 48 | -o $@ $(SERVICE_SOURCES) $^ $(SERVICE_LIBS) $(CDMI_LFLAGSS) 49 | 50 | clean: clean_cdmi 51 | rm -f $(RPC_OBJECTS) 52 | rm -f service 53 | 54 | ifndef CDMI_MAKE_CONFIG 55 | include config_clearkey.mk 56 | else 57 | include $(CDMI_MAKE_CONFIG) 58 | endif 59 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_callback_xdr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include "opencdm_callback.h" 7 | 8 | bool_t 9 | xdr_rpc_cb_message (XDR *xdrs, rpc_cb_message *objp) 10 | { 11 | register int32_t *buf; 12 | 13 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 14 | sizeof (char), (xdrproc_t) xdr_char)) 15 | return FALSE; 16 | if (!xdr_string (xdrs, &objp->message, ~0)) 17 | return FALSE; 18 | if (!xdr_string (xdrs, &objp->destination_url, ~0)) 19 | return FALSE; 20 | return TRUE; 21 | } 22 | 23 | bool_t 24 | xdr_rpc_cb_key_status_update (XDR *xdrs, rpc_cb_key_status_update *objp) 25 | { 26 | register int32_t *buf; 27 | 28 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 29 | sizeof (char), (xdrproc_t) xdr_char)) 30 | return FALSE; 31 | if (!xdr_string (xdrs, &objp->message, ~0)) 32 | return FALSE; 33 | return TRUE; 34 | } 35 | 36 | bool_t 37 | xdr_rpc_cb_ready (XDR *xdrs, rpc_cb_ready *objp) 38 | { 39 | register int32_t *buf; 40 | 41 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 42 | sizeof (char), (xdrproc_t) xdr_char)) 43 | return FALSE; 44 | return TRUE; 45 | } 46 | 47 | bool_t 48 | xdr_rpc_cb_error (XDR *xdrs, rpc_cb_error *objp) 49 | { 50 | register int32_t *buf; 51 | 52 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 53 | sizeof (char), (xdrproc_t) xdr_char)) 54 | return FALSE; 55 | if (!xdr_int (xdrs, &objp->error)) 56 | return FALSE; 57 | if (!xdr_string (xdrs, &objp->error_message, ~0)) 58 | return FALSE; 59 | return TRUE; 60 | } 61 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_callback_clnt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include /* for memset */ 7 | #include "opencdm_callback.h" 8 | 9 | /* Default timeout can be changed using clnt_control() */ 10 | static struct timeval TIMEOUT = { 25, 0 }; 11 | 12 | void * 13 | on_message_1(rpc_cb_message *argp, CLIENT *clnt) 14 | { 15 | static char clnt_res; 16 | 17 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 18 | if (clnt_call (clnt, ON_MESSAGE, 19 | (xdrproc_t) xdr_rpc_cb_message, (caddr_t) argp, 20 | (xdrproc_t) xdr_void, (caddr_t) &clnt_res, 21 | TIMEOUT) != RPC_SUCCESS) { 22 | return (NULL); 23 | } 24 | return ((void *)&clnt_res); 25 | } 26 | 27 | void * 28 | on_ready_1(rpc_cb_ready *argp, CLIENT *clnt) 29 | { 30 | static char clnt_res; 31 | 32 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 33 | if (clnt_call (clnt, ON_READY, 34 | (xdrproc_t) xdr_rpc_cb_ready, (caddr_t) argp, 35 | (xdrproc_t) xdr_void, (caddr_t) &clnt_res, 36 | TIMEOUT) != RPC_SUCCESS) { 37 | return (NULL); 38 | } 39 | return ((void *)&clnt_res); 40 | } 41 | 42 | void * 43 | on_error_1(rpc_cb_error *argp, CLIENT *clnt) 44 | { 45 | static char clnt_res; 46 | 47 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 48 | if (clnt_call (clnt, ON_ERROR, 49 | (xdrproc_t) xdr_rpc_cb_error, (caddr_t) argp, 50 | (xdrproc_t) xdr_void, (caddr_t) &clnt_res, 51 | TIMEOUT) != RPC_SUCCESS) { 52 | return (NULL); 53 | } 54 | return ((void *)&clnt_res); 55 | } 56 | 57 | void * 58 | on_key_status_update_1(rpc_cb_key_status_update *argp, CLIENT *clnt) 59 | { 60 | static char clnt_res; 61 | 62 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 63 | if (clnt_call (clnt, ON_KEY_STATUS_UPDATE, 64 | (xdrproc_t) xdr_rpc_cb_key_status_update, (caddr_t) argp, 65 | (xdrproc_t) xdr_void, (caddr_t) &clnt_res, 66 | TIMEOUT) != RPC_SUCCESS) { 67 | return (NULL); 68 | } 69 | return ((void *)&clnt_res); 70 | } 71 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/jsmn.h: -------------------------------------------------------------------------------- 1 | #ifndef __JSMN_H_ 2 | #define __JSMN_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * JSON type identifier. Basic types are: 12 | * o Object 13 | * o Array 14 | * o String 15 | * o Other primitive: number, boolean (true/false) or null 16 | */ 17 | typedef enum { 18 | JSMN_PRIMITIVE = 0, 19 | JSMN_OBJECT = 1, 20 | JSMN_ARRAY = 2, 21 | JSMN_STRING = 3 22 | } jsmntype_t; 23 | 24 | typedef enum { 25 | /* Not enough tokens were provided */ 26 | JSMN_ERROR_NOMEM = -1, 27 | /* Invalid character inside JSON string */ 28 | JSMN_ERROR_INVAL = -2, 29 | /* The string is not a full JSON packet, more bytes expected */ 30 | JSMN_ERROR_PART = -3 31 | } jsmnerr_t; 32 | 33 | /** 34 | * JSON token description. 35 | * @param type type (object, array, string etc.) 36 | * @param start start position in JSON data string 37 | * @param end end position in JSON data string 38 | */ 39 | typedef struct { 40 | jsmntype_t type; 41 | int start; 42 | int end; 43 | int size; 44 | #ifdef JSMN_PARENT_LINKS 45 | int parent; 46 | #endif 47 | } jsmntok_t; 48 | 49 | /** 50 | * JSON parser. Contains an array of token blocks available. Also stores 51 | * the string being parsed now and current position in that string 52 | */ 53 | typedef struct { 54 | unsigned int pos; /* offset in the JSON string */ 55 | unsigned int toknext; /* next token to allocate */ 56 | int toksuper; /* superior token node, e.g parent object or array */ 57 | } jsmn_parser; 58 | 59 | /** 60 | * Create JSON parser over an array of tokens 61 | */ 62 | void jsmn_init(jsmn_parser *parser); 63 | 64 | /** 65 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing 66 | * a single JSON object. 67 | */ 68 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 69 | jsmntok_t *tokens, unsigned int num_tokens); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif /* __JSMN_H_ */ 76 | -------------------------------------------------------------------------------- /cdmi-stub/json_web_key.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /* Copyright 2015 Linaro Ltd 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | #ifndef OPENCDM_CDMI_STUB_JSON_WEB_KEY_H 21 | #define OPENCDM_CDMI_STUB_JSON_WEB_KEY_H 22 | #include "keypairs.h" 23 | 24 | namespace media{ 25 | 26 | const char kKeysTag[] = "keys"; 27 | const char kKeyTypeTag[] = "kty"; 28 | const char kKeyTypeOct[] = "oct"; // Octet sequence. 29 | const char kAlgTag[] = "alg"; 30 | const char kAlgA128KW[] = "A128KW"; // AES key wrap using a 128-bit key. 31 | const char kKeyTag[] = "k"; 32 | const char kKeyIdTag[] = "kid"; 33 | const char kKeyIdsTag[] = "kids"; 34 | const char kBase64Padding = '='; 35 | const char kTypeTag[] = "type"; 36 | const char kTemporarySession[] = "temporary"; 37 | const char kPersistentLicenseSession[] = "persistent-license"; 38 | const char kPersistentReleaseMessageSession[] = "persistent-release-message"; 39 | 40 | // Based on the corresponding chromium source 41 | 42 | // Extracts the JSON Web Keys from a JSON Web Key Set. If |input| looks like 43 | // a valid JWK Set, then true is returned and |keys| and |session_type| are 44 | // updated to contain the values found. Otherwise return false. 45 | bool ExtractKeysFromJWKSet(const std::string& jwk_set, 46 | KeyIdAndKeyPairs* keys, 47 | int session_type); 48 | 49 | 50 | }//namespace media 51 | #endif 52 | -------------------------------------------------------------------------------- /libs/shmemsem/shmemsem_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * based on Keith Gaughan - Shared Memory and Semaphores - March 22, 2003 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if !defined(__GNU_LIBRARY__) || defined(_SEM_SEMUN_UNDEFINED) 27 | union semun 28 | { 29 | int val; // value for SETVAL 30 | struct semid_ds* buf; // buffer for IPC_STAT, IPC_SET 31 | unsigned short* array; // array for GETALL, SETALL 32 | struct seminfo* __buf; // buffer for IPC_INFO 33 | }; 34 | #endif 35 | 36 | #ifndef SHMEM_SEM_HELPER 37 | #define SHMEM_SEM_HELPER 38 | 39 | /* 40 | * FAMIUM SPECIFIC 41 | */ 42 | 43 | // static info exchange shmem 44 | struct shmem_info { 45 | int32_t idSidShMem; 46 | int32_t idIvShMem; 47 | int32_t idSampleShMem; 48 | int32_t idSubsampleDataShMem; 49 | uint32_t sidSize; 50 | uint32_t ivSize; 51 | uint32_t sampleSize; 52 | uint32_t subsampleDataSize; 53 | }; 54 | 55 | enum { 56 | SEM_XCHNG_PUSH, 57 | SEM_XCHNG_DECRYPT, 58 | SEM_XCHNG_PULL 59 | }; 60 | 61 | /* 62 | * GENERAL SHARED MEM AND SEMAPHORES 63 | */ 64 | 65 | // Declarations for wrapper functions... 66 | int AllocateSharedMemory(int n); 67 | void* MapSharedMemory(int id); 68 | void* MapExistingSharedMemory(int id, void* existingAddr); 69 | int DetachExistingSharedMemory(void* existingAddr); 70 | int CreateSemaphoreSet(int n, unsigned short* vals); 71 | void DeleteSemaphoreSet(int id); 72 | void LockSemaphore(int id, int i); 73 | void UnlockSemaphore(int id, int i); 74 | 75 | #endif /* SHMEM_SEM_HELPER */ 76 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/example/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../jsmn.h" 4 | 5 | /* 6 | * A small example of jsmn parsing when JSON structure is known and number of 7 | * tokens is predictable. 8 | */ 9 | 10 | const char *JSON_STRING = 11 | "{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " 12 | "\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}"; 13 | 14 | static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { 15 | if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && 16 | strncmp(json + tok->start, s, tok->end - tok->start) == 0) { 17 | return 0; 18 | } 19 | return -1; 20 | } 21 | 22 | int main() { 23 | int i; 24 | int r; 25 | jsmn_parser p; 26 | jsmntok_t t[128]; /* We expect no more than 128 tokens */ 27 | 28 | jsmn_init(&p); 29 | r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0])); 30 | if (r < 0) { 31 | printf("Failed to parse JSON: %d\n", r); 32 | return 1; 33 | } 34 | 35 | /* Assume the top-level element is an object */ 36 | if (r < 1 || t[0].type != JSMN_OBJECT) { 37 | printf("Object expected\n"); 38 | return 1; 39 | } 40 | 41 | /* Loop over all keys of the root object */ 42 | for (i = 1; i < r; i++) { 43 | if (jsoneq(JSON_STRING, &t[i], "user") == 0) { 44 | /* We may use strndup() to fetch string value */ 45 | printf("- User: %.*s\n", t[i+1].end-t[i+1].start, 46 | JSON_STRING + t[i+1].start); 47 | i++; 48 | } else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) { 49 | /* We may additionally check if the value is either "true" or "false" */ 50 | printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start, 51 | JSON_STRING + t[i+1].start); 52 | i++; 53 | } else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) { 54 | /* We may want to do strtol() here to get numeric value */ 55 | printf("- UID: %.*s\n", t[i+1].end-t[i+1].start, 56 | JSON_STRING + t[i+1].start); 57 | i++; 58 | } else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) { 59 | int j; 60 | printf("- Groups:\n"); 61 | if (t[i+1].type != JSMN_ARRAY) { 62 | continue; /* We expect groups to be an array of strings */ 63 | } 64 | for (j = 0; j < t[i+1].size; j++) { 65 | jsmntok_t *g = &t[i+j+2]; 66 | printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start); 67 | } 68 | i += t[i+1].size + 1; 69 | } else { 70 | printf("Unexpected key: %.*s\n", t[i].end-t[i].start, 71 | JSON_STRING + t[i].start); 72 | } 73 | } 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /rpc/opencdm_xdr.x: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * OpenCDM XDR to be used for RPC communication between CDM and DRM platform counterpart 16 | * Based on EME methods and naming. 17 | */ 18 | 19 | /* 20 | * REQUEST DATA TYPES 21 | */ 22 | 23 | struct rpc_request_is_type_supported { 24 | char key_system <>; 25 | char mime_type <>; 26 | }; 27 | 28 | struct rpc_request_mediakeys { 29 | char key_system <>; 30 | }; 31 | 32 | struct rpc_request_callback_info { 33 | char hostname <>; 34 | uint64_t prog_num; 35 | uint32_t prog_version; 36 | }; 37 | 38 | struct rpc_request_create_session { 39 | char init_data_type <>; 40 | uint8_t init_data <>; 41 | rpc_request_callback_info callback_info; 42 | }; 43 | 44 | struct rpc_request_load_session { 45 | char session_id <>; 46 | }; 47 | 48 | struct rpc_request_session_update { 49 | char session_id <>; 50 | uint8_t key <>; 51 | }; 52 | 53 | struct rpc_request_session_release { 54 | char session_id <>; 55 | }; 56 | 57 | struct rpc_request_mediaengine_data { 58 | char session_id <>; 59 | uint8_t auth_data <>; 60 | int32_t id_exchange_shmem; 61 | int32_t id_exchange_sem; 62 | }; 63 | 64 | /* 65 | * RESPONSE DATA TYPES 66 | */ 67 | 68 | struct rpc_response_generic { 69 | int platform_val; 70 | }; 71 | 72 | struct rpc_response_create_session { 73 | int platform_val; 74 | char session_id <>; 75 | }; 76 | 77 | program OPEN_CDM { 78 | version OPEN_CDM_EME_5 { 79 | rpc_response_generic RPC_OPEN_CDM_IS_TYPE_SUPPORTED(rpc_request_is_type_supported) = 1; 80 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYS(rpc_request_mediakeys) = 2; 81 | rpc_response_create_session RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION(rpc_request_create_session) = 3; 82 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION(rpc_request_load_session) = 4; 83 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE(rpc_request_session_update) = 5; 84 | rpc_response_generic RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE(rpc_request_session_release) = 6; 85 | rpc_response_generic RPC_OPEN_CDM_MEDIAENGINE(rpc_request_mediaengine_data) = 7; 86 | } = 1; 87 | } = 0x61135687; /* FAMEFHG */ 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Content Decryption Module CDMi 2 | 3 | The Open Content Decryption Module CDMi is to be used with the [Open Content Decryption Module (OCDM)](https://github.com/fraunhoferfokus/open-content-decryption-module). It is meant to run as a service connecting the webbrowser with the actual CDM. Any CDM that follows the Content Decryption Module Interface (CDMi) [1] can be used with this service. Together, both components enable DRM interoperability to be used with HTML5 based browser environments [2] according to W3C EME [3] specification. 4 | 5 | This repository provides a bare bones implementation to demonstrate how a CDM can be adapted to be used with OCDM through the CDMi interface. The implementation provides the complete flow between webbrowser and CDM. 6 | 7 | OCDM is built with a modular concept in mind to be easily extendible and flexible. For more information see the the OCDM repository. 8 | 9 | Fraunhofer FOKUS has developed the Open Content Decryption Module (OCDM) according to W3C EME specification to be used with HTML5 based browser environments. The implementation enables DRM interoperability. We would be happy to see a broad adoption of our implementation and encourage contributions. A first e2e implementation has been done testing with a Microsoft PlayReady CDMi implementation. 10 | 11 | ## Introduction / Purpose / Why this? 12 | 13 | * Interoperable HTML5 based protected video delivery 14 | * DRM interoperability 15 | * CENC, DASH 16 | * Plugin based integration (Pepper Plugin API) 17 | * CDMi allows open source browsers to support DRM without licensing it 18 | * e2e tested with Microsoft PlayReady DRM system 19 | 20 | ## References 21 | 22 | * [1] Content Decryption Module Interface Specification http://download.microsoft.com/download/E/A/4/EA470677-6C3C-4AFE-8A86-A196ADFD0F78/Content%20Decryption%20Module%20Interface%20Specification.pdf 23 | * [2] Fraunhofer FOKUS FAMIUM DRM http://www.fokus.fraunhofer.de/en/fame/Solutions/DRM/index.html 24 | * [3] W3C Encrypted Media Extensions https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html 25 | 26 | ## Supported Platforms 27 | 28 | The used development platform is Ubuntu Linux 14.04 64-bit, should work fine on most other Linux' as well. 29 | 30 | ## How to build 31 | 32 | * clone this repository 33 | * compile with GNU make 34 | * ```$ make``` 35 | 36 | ## How to run 37 | 38 | * ```$ ./service``` 39 | 40 | ## Known Issues / Comments 41 | 42 | This is a preliminary release. Please file any issues or comments. 43 | 44 | * TODO: add session-load support 45 | * TODO: use named pipes for IPC 46 | * Code needs more review (e.g. memory allocation, appropriate data types). 47 | 48 | Please see the OCDM repository for further information [github.com/fraunhoferfokus/open-content-decryption-module](https://github.com/fraunhoferfokus/open-content-decryption-module). 49 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/example/jsondump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../jsmn.h" 6 | 7 | /* 8 | * An example of reading JSON from stdin and printing its content to stdout. 9 | * The output looks like YAML, but I'm not sure if it's really compatible. 10 | */ 11 | 12 | static int dump(const char *js, jsmntok_t *t, size_t count, int indent) { 13 | int i, j, k; 14 | if (count == 0) { 15 | return 0; 16 | } 17 | if (t->type == JSMN_PRIMITIVE) { 18 | printf("%.*s", t->end - t->start, js+t->start); 19 | return 1; 20 | } else if (t->type == JSMN_STRING) { 21 | printf("'%.*s'", t->end - t->start, js+t->start); 22 | return 1; 23 | } else if (t->type == JSMN_OBJECT) { 24 | printf("\n"); 25 | j = 0; 26 | for (i = 0; i < t->size; i++) { 27 | for (k = 0; k < indent; k++) printf(" "); 28 | j += dump(js, t+1+j, count-j, indent+1); 29 | printf(": "); 30 | j += dump(js, t+1+j, count-j, indent+1); 31 | printf("\n"); 32 | } 33 | return j+1; 34 | } else if (t->type == JSMN_ARRAY) { 35 | j = 0; 36 | printf("\n"); 37 | for (i = 0; i < t->size; i++) { 38 | for (k = 0; k < indent-1; k++) printf(" "); 39 | printf(" - "); 40 | j += dump(js, t+1+j, count-j, indent+1); 41 | printf("\n"); 42 | } 43 | return j+1; 44 | } 45 | return 0; 46 | } 47 | 48 | int main() { 49 | int r; 50 | int eof_expected = 0; 51 | char *js = NULL; 52 | size_t jslen = 0; 53 | char buf[BUFSIZ]; 54 | 55 | jsmn_parser p; 56 | jsmntok_t *tok; 57 | size_t tokcount = 2; 58 | 59 | /* Prepare parser */ 60 | jsmn_init(&p); 61 | 62 | /* Allocate some tokens as a start */ 63 | tok = malloc(sizeof(*tok) * tokcount); 64 | if (tok == NULL) { 65 | fprintf(stderr, "malloc(): errno=%d\n", errno); 66 | return 3; 67 | } 68 | 69 | for (;;) { 70 | /* Read another chunk */ 71 | r = fread(buf, 1, sizeof(buf), stdin); 72 | if (r < 0) { 73 | fprintf(stderr, "fread(): %d, errno=%d\n", r, errno); 74 | return 1; 75 | } 76 | if (r == 0) { 77 | if (eof_expected != 0) { 78 | return 0; 79 | } else { 80 | fprintf(stderr, "fread(): unexpected EOF\n"); 81 | return 2; 82 | } 83 | } 84 | 85 | js = realloc(js, jslen + r + 1); 86 | if (js == NULL) { 87 | fprintf(stderr, "realloc(): errno=%d\n", errno); 88 | return 3; 89 | } 90 | strncpy(js + jslen, buf, r); 91 | jslen = jslen + r; 92 | 93 | again: 94 | r = jsmn_parse(&p, js, jslen, tok, tokcount); 95 | if (r < 0) { 96 | if (r == JSMN_ERROR_NOMEM) { 97 | tokcount = tokcount * 2; 98 | tok = realloc(tok, sizeof(*tok) * tokcount); 99 | if (tok == NULL) { 100 | fprintf(stderr, "realloc(): errno=%d\n", errno); 101 | return 3; 102 | } 103 | goto again; 104 | } 105 | } else { 106 | dump(js, tok, p.toknext, 0); 107 | eof_expected = 1; 108 | } 109 | } 110 | 111 | return 0; 112 | } 113 | -------------------------------------------------------------------------------- /cdmi-stub/mediakeys.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "imp.h" 20 | 21 | using namespace std; 22 | 23 | namespace CDMi { 24 | 25 | uint32_t CMediaKeys::s_sessionCnt = 10; 26 | 27 | CMediaKeys::CMediaKeys() {} 28 | 29 | CMediaKeys::~CMediaKeys(void) {} 30 | 31 | bool CMediaKeys::IsTypeSupported( 32 | const char *f_pwszMimeType, 33 | const char *f_pwszKeySystem) const { 34 | bool isSupported = true; 35 | } 36 | 37 | const char * CMediaKeys::CreateSessionId() { 38 | const char *tmp; 39 | stringstream strs; 40 | strs << s_sessionCnt; 41 | string tmp_str = strs.str(); 42 | tmp = tmp_str.c_str(); 43 | 44 | char *buffer = new char[tmp_str.length()](); 45 | strcpy(buffer, tmp); 46 | 47 | s_sessionCnt += 1; 48 | 49 | return const_cast(buffer); 50 | } 51 | 52 | CDMi_RESULT CMediaKeys::CreateMediaKeySession( 53 | const char *f_pwszMimeType, 54 | const uint8_t *f_pbInitData, 55 | uint32_t f_cbInitData, 56 | const uint8_t *f_pbCDMData, 57 | uint32_t f_cbCDMData, 58 | IMediaKeySession **f_ppiMediaKeySession) { 59 | cout << "#CMediaKeys::CreateMediaKeySession" << endl; 60 | 61 | CDMi_RESULT dr = CDMi_S_FALSE; 62 | CMediaKeySession *poMediaKeySession = NULL; 63 | 64 | *f_ppiMediaKeySession = NULL; 65 | 66 | poMediaKeySession = new CMediaKeySession(CMediaKeys::CreateSessionId()); 67 | 68 | cout << "#CMediaKeys::CreateMediaKeySession: created new CMediaKeySession" << endl; 69 | 70 | dr = poMediaKeySession->Init(f_pbInitData, 71 | f_cbInitData, 72 | f_pbCDMData, 73 | f_cbCDMData); 74 | 75 | *f_ppiMediaKeySession = static_cast(poMediaKeySession); 76 | 77 | if (dr != CDMi_SUCCESS) { 78 | delete poMediaKeySession; 79 | } 80 | return dr; 81 | } 82 | 83 | CDMi_RESULT CMediaKeys::DestroyMediaKeySession( 84 | IMediaKeySession *f_piMediaKeySession) { 85 | CDMi_RESULT dr = CDMi_SUCCESS; 86 | 87 | delete f_piMediaKeySession; 88 | 89 | return dr; 90 | } 91 | 92 | CDMi_RESULT CreateMediaKeys(IMediaKeys **f_ppiMediaKeys) { 93 | CMediaKeys *pMediaKeys = NULL; 94 | *f_ppiMediaKeys = NULL; 95 | 96 | pMediaKeys = new CMediaKeys(); 97 | *f_ppiMediaKeys = static_cast(pMediaKeys); 98 | 99 | return CDMi_SUCCESS; 100 | } 101 | 102 | CDMi_RESULT DestroyMediaKeys(IMediaKeys *f_piMediaKeys) { 103 | CDMi_RESULT dr = CDMi_SUCCESS; 104 | 105 | delete f_piMediaKeys; 106 | 107 | return dr; 108 | } 109 | 110 | } // namespace CDMi 111 | -------------------------------------------------------------------------------- /cdmi-stub/mediaenginesession.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include 15 | 16 | #include "imp.h" 17 | #include "json_web_key.h" 18 | 19 | using namespace std; 20 | 21 | namespace CDMi { 22 | 23 | CMediaEngineSession::CMediaEngineSession(void) : 24 | m_pbSessionKey(NULL), 25 | m_cbSessionKey(0), 26 | m_pMediaKeySession(NULL) {} 27 | 28 | CMediaEngineSession::~CMediaEngineSession(void) {} 29 | 30 | CDMi_RESULT CMediaEngineSession::Decrypt( 31 | uint32_t f_cdwSubSampleMapping, 32 | const uint32_t *f_pdwSubSampleMapping, 33 | uint32_t f_cbIV, 34 | const uint8_t *f_pbIV, 35 | uint32_t f_cbData, 36 | const uint8_t *f_pbData, 37 | uint32_t *f_pcbOpaqueClearContent, 38 | uint8_t **f_ppbOpaqueClearContent) { 39 | 40 | CMediaKeySession *pMediaKeySession = 41 | static_cast(m_pMediaKeySession); 42 | 43 | return pMediaKeySession->Decrypt(m_pbSessionKey, 44 | m_cbSessionKey, 45 | f_pdwSubSampleMapping, 46 | f_cdwSubSampleMapping, 47 | f_pbIV, 48 | f_cbIV, 49 | f_pbData, 50 | f_cbData, 51 | f_pcbOpaqueClearContent, 52 | f_ppbOpaqueClearContent 53 | ); 54 | 55 | } 56 | CDMi_RESULT CMediaEngineSession::ReleaseClearContent( 57 | const uint32_t f_cbClearContentOpaque, 58 | uint8_t *f_pbClearContentOpaque ){ 59 | 60 | CMediaKeySession *pMediaKeySession = 61 | static_cast(m_pMediaKeySession); 62 | 63 | return pMediaKeySession->ReleaseClearContent(m_pbSessionKey, 64 | m_cbSessionKey, 65 | f_cbClearContentOpaque, 66 | f_pbClearContentOpaque); 67 | 68 | } 69 | 70 | 71 | CDMi_RESULT CMediaEngineSession::Init( 72 | const IMediaKeySession *f_piMediaKeySession, 73 | const uint8_t *f_pbSessionKey, 74 | uint32_t f_cbSessionKey) { 75 | m_pMediaKeySession = const_cast(f_piMediaKeySession); 76 | 77 | return CDMi_SUCCESS; 78 | } 79 | 80 | CDMi_RESULT CreateMediaEngineSession( 81 | IMediaKeySession *f_piMediaKeySession, 82 | IMediaEngineSession **f_ppiMediaEngineSession) { 83 | CMediaEngineSession *poMediaEngineSession = NULL; 84 | 85 | poMediaEngineSession = new CMediaEngineSession(); 86 | 87 | // Pass the clear AES session key to MediaEngineSession for sample protection. 88 | poMediaEngineSession->Init(f_piMediaKeySession, 0, 0); 89 | 90 | *f_ppiMediaEngineSession = 91 | static_cast(poMediaEngineSession); 92 | 93 | return CDMi_SUCCESS; 94 | } 95 | 96 | CDMi_RESULT DestroyMediaEngineSession( 97 | IMediaEngineSession *f_piMediaEngineSession) {} 98 | 99 | } // namespace CDMi 100 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_callback.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #ifndef _OPENCDM_CALLBACK_H_RPCGEN 7 | #define _OPENCDM_CALLBACK_H_RPCGEN 8 | 9 | #include 10 | 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | 17 | struct rpc_cb_message { 18 | struct { 19 | u_int session_id_len; 20 | char *session_id_val; 21 | } session_id; 22 | char *message; 23 | char *destination_url; 24 | }; 25 | typedef struct rpc_cb_message rpc_cb_message; 26 | 27 | struct rpc_cb_key_status_update { 28 | struct { 29 | u_int session_id_len; 30 | char *session_id_val; 31 | } session_id; 32 | char *message; 33 | }; 34 | typedef struct rpc_cb_key_status_update rpc_cb_key_status_update; 35 | 36 | struct rpc_cb_ready { 37 | struct { 38 | u_int session_id_len; 39 | char *session_id_val; 40 | } session_id; 41 | }; 42 | typedef struct rpc_cb_ready rpc_cb_ready; 43 | 44 | struct rpc_cb_error { 45 | struct { 46 | u_int session_id_len; 47 | char *session_id_val; 48 | } session_id; 49 | int error; 50 | char *error_message; 51 | }; 52 | typedef struct rpc_cb_error rpc_cb_error; 53 | 54 | #define OPEN_CDM_CALLBACK 0x66666666 55 | #define OPEN_CDM_EME_5 1 56 | 57 | #if defined(__STDC__) || defined(__cplusplus) 58 | #define ON_MESSAGE 1 59 | extern void * on_message_1(rpc_cb_message *, CLIENT *); 60 | extern void * on_message_1_svc(rpc_cb_message *, struct svc_req *); 61 | #define ON_READY 2 62 | extern void * on_ready_1(rpc_cb_ready *, CLIENT *); 63 | extern void * on_ready_1_svc(rpc_cb_ready *, struct svc_req *); 64 | #define ON_ERROR 3 65 | extern void * on_error_1(rpc_cb_error *, CLIENT *); 66 | extern void * on_error_1_svc(rpc_cb_error *, struct svc_req *); 67 | #define ON_KEY_STATUS_UPDATE 4 68 | extern void * on_key_status_update_1(rpc_cb_key_status_update *, CLIENT *); 69 | extern void * on_key_status_update_1_svc(rpc_cb_key_status_update *, struct svc_req *); 70 | extern int open_cdm_callback_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); 71 | 72 | #else /* K&R C */ 73 | #define ON_MESSAGE 1 74 | extern void * on_message_1(); 75 | extern void * on_message_1_svc(); 76 | #define ON_READY 2 77 | extern void * on_ready_1(); 78 | extern void * on_ready_1_svc(); 79 | #define ON_ERROR 3 80 | extern void * on_error_1(); 81 | extern void * on_error_1_svc(); 82 | #define ON_KEY_STATUS_UPDATE 4 83 | extern void * on_key_status_update_1(); 84 | extern void * on_key_status_update_1_svc(); 85 | extern int open_cdm_callback_1_freeresult (); 86 | #endif /* K&R C */ 87 | 88 | /* the xdr functions */ 89 | 90 | #if defined(__STDC__) || defined(__cplusplus) 91 | extern bool_t xdr_rpc_cb_message (XDR *, rpc_cb_message*); 92 | extern bool_t xdr_rpc_cb_key_status_update (XDR *, rpc_cb_key_status_update*); 93 | extern bool_t xdr_rpc_cb_ready (XDR *, rpc_cb_ready*); 94 | extern bool_t xdr_rpc_cb_error (XDR *, rpc_cb_error*); 95 | 96 | #else /* K&R C */ 97 | extern bool_t xdr_rpc_cb_message (); 98 | extern bool_t xdr_rpc_cb_key_status_update (); 99 | extern bool_t xdr_rpc_cb_ready (); 100 | extern bool_t xdr_rpc_cb_error (); 101 | 102 | #endif /* K&R C */ 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | 108 | #endif /* !_OPENCDM_CALLBACK_H_RPCGEN */ 109 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_callback_svc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include "opencdm_callback.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef SIG_PF 16 | #define SIG_PF void(*)(int) 17 | #endif 18 | 19 | static void 20 | open_cdm_callback_1(struct svc_req *rqstp, register SVCXPRT *transp) 21 | { 22 | union { 23 | rpc_cb_message on_message_1_arg; 24 | rpc_cb_ready on_ready_1_arg; 25 | rpc_cb_error on_error_1_arg; 26 | rpc_cb_key_status_update on_key_status_update_1_arg; 27 | } argument; 28 | char *result; 29 | xdrproc_t _xdr_argument, _xdr_result; 30 | char *(*local)(char *, struct svc_req *); 31 | 32 | switch (rqstp->rq_proc) { 33 | case NULLPROC: 34 | (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); 35 | return; 36 | 37 | case ON_MESSAGE: 38 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_message; 39 | _xdr_result = (xdrproc_t) xdr_void; 40 | local = (char *(*)(char *, struct svc_req *)) on_message_1_svc; 41 | break; 42 | 43 | case ON_READY: 44 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_ready; 45 | _xdr_result = (xdrproc_t) xdr_void; 46 | local = (char *(*)(char *, struct svc_req *)) on_ready_1_svc; 47 | break; 48 | 49 | case ON_ERROR: 50 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_error; 51 | _xdr_result = (xdrproc_t) xdr_void; 52 | local = (char *(*)(char *, struct svc_req *)) on_error_1_svc; 53 | break; 54 | 55 | case ON_KEY_STATUS_UPDATE: 56 | _xdr_argument = (xdrproc_t) xdr_rpc_cb_key_status_update; 57 | _xdr_result = (xdrproc_t) xdr_void; 58 | local = (char *(*)(char *, struct svc_req *)) on_key_status_update_1_svc; 59 | break; 60 | 61 | default: 62 | svcerr_noproc (transp); 63 | return; 64 | } 65 | memset ((char *)&argument, 0, sizeof (argument)); 66 | if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 67 | svcerr_decode (transp); 68 | return; 69 | } 70 | result = (*local)((char *)&argument, rqstp); 71 | if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { 72 | svcerr_systemerr (transp); 73 | } 74 | if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 75 | fprintf (stderr, "%s", "unable to free arguments"); 76 | exit (1); 77 | } 78 | return; 79 | } 80 | 81 | int 82 | main (int argc, char **argv) 83 | { 84 | register SVCXPRT *transp; 85 | 86 | pmap_unset (OPEN_CDM_CALLBACK, OPEN_CDM_EME_5); 87 | 88 | transp = svcudp_create(RPC_ANYSOCK); 89 | if (transp == NULL) { 90 | fprintf (stderr, "%s", "cannot create udp service."); 91 | exit(1); 92 | } 93 | if (!svc_register(transp, OPEN_CDM_CALLBACK, OPEN_CDM_EME_5, open_cdm_callback_1, IPPROTO_UDP)) { 94 | fprintf (stderr, "%s", "unable to register (OPEN_CDM_CALLBACK, OPEN_CDM_EME_5, udp)."); 95 | exit(1); 96 | } 97 | 98 | transp = svctcp_create(RPC_ANYSOCK, 0, 0); 99 | if (transp == NULL) { 100 | fprintf (stderr, "%s", "cannot create tcp service."); 101 | exit(1); 102 | } 103 | if (!svc_register(transp, OPEN_CDM_CALLBACK, OPEN_CDM_EME_5, open_cdm_callback_1, IPPROTO_TCP)) { 104 | fprintf (stderr, "%s", "unable to register (OPEN_CDM_CALLBACK, OPEN_CDM_EME_5, tcp)."); 105 | exit(1); 106 | } 107 | 108 | svc_run (); 109 | fprintf (stderr, "%s", "svc_run returned"); 110 | exit (1); 111 | /* NOTREACHED */ 112 | } 113 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_xdr_clnt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include /* for memset */ 7 | #include "opencdm_xdr.h" 8 | 9 | /* Default timeout can be changed using clnt_control() */ 10 | static struct timeval TIMEOUT = { 25, 0 }; 11 | 12 | rpc_response_generic * 13 | rpc_open_cdm_is_type_supported_1(rpc_request_is_type_supported *argp, CLIENT *clnt) 14 | { 15 | static rpc_response_generic clnt_res; 16 | 17 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 18 | if (clnt_call (clnt, RPC_OPEN_CDM_IS_TYPE_SUPPORTED, 19 | (xdrproc_t) xdr_rpc_request_is_type_supported, (caddr_t) argp, 20 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 21 | TIMEOUT) != RPC_SUCCESS) { 22 | return (NULL); 23 | } 24 | return (&clnt_res); 25 | } 26 | 27 | rpc_response_generic * 28 | rpc_open_cdm_mediakeys_1(rpc_request_mediakeys *argp, CLIENT *clnt) 29 | { 30 | static rpc_response_generic clnt_res; 31 | 32 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 33 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYS, 34 | (xdrproc_t) xdr_rpc_request_mediakeys, (caddr_t) argp, 35 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 36 | TIMEOUT) != RPC_SUCCESS) { 37 | return (NULL); 38 | } 39 | return (&clnt_res); 40 | } 41 | 42 | rpc_response_create_session * 43 | rpc_open_cdm_mediakeys_create_session_1(rpc_request_create_session *argp, CLIENT *clnt) 44 | { 45 | static rpc_response_create_session clnt_res; 46 | 47 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 48 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION, 49 | (xdrproc_t) xdr_rpc_request_create_session, (caddr_t) argp, 50 | (xdrproc_t) xdr_rpc_response_create_session, (caddr_t) &clnt_res, 51 | TIMEOUT) != RPC_SUCCESS) { 52 | return (NULL); 53 | } 54 | return (&clnt_res); 55 | } 56 | 57 | rpc_response_generic * 58 | rpc_open_cdm_mediakeys_load_session_1(rpc_request_load_session *argp, CLIENT *clnt) 59 | { 60 | static rpc_response_generic clnt_res; 61 | 62 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 63 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION, 64 | (xdrproc_t) xdr_rpc_request_load_session, (caddr_t) argp, 65 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 66 | TIMEOUT) != RPC_SUCCESS) { 67 | return (NULL); 68 | } 69 | return (&clnt_res); 70 | } 71 | 72 | rpc_response_generic * 73 | rpc_open_cdm_mediakeysession_update_1(rpc_request_session_update *argp, CLIENT *clnt) 74 | { 75 | static rpc_response_generic clnt_res; 76 | 77 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 78 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE, 79 | (xdrproc_t) xdr_rpc_request_session_update, (caddr_t) argp, 80 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 81 | TIMEOUT) != RPC_SUCCESS) { 82 | return (NULL); 83 | } 84 | return (&clnt_res); 85 | } 86 | 87 | rpc_response_generic * 88 | rpc_open_cdm_mediakeysession_release_1(rpc_request_session_release *argp, CLIENT *clnt) 89 | { 90 | static rpc_response_generic clnt_res; 91 | 92 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 93 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE, 94 | (xdrproc_t) xdr_rpc_request_session_release, (caddr_t) argp, 95 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 96 | TIMEOUT) != RPC_SUCCESS) { 97 | return (NULL); 98 | } 99 | return (&clnt_res); 100 | } 101 | 102 | rpc_response_generic * 103 | rpc_open_cdm_mediaengine_1(rpc_request_mediaengine_data *argp, CLIENT *clnt) 104 | { 105 | static rpc_response_generic clnt_res; 106 | 107 | memset((char *)&clnt_res, 0, sizeof(clnt_res)); 108 | if (clnt_call (clnt, RPC_OPEN_CDM_MEDIAENGINE, 109 | (xdrproc_t) xdr_rpc_request_mediaengine_data, (caddr_t) argp, 110 | (xdrproc_t) xdr_rpc_response_generic, (caddr_t) &clnt_res, 111 | TIMEOUT) != RPC_SUCCESS) { 112 | return (NULL); 113 | } 114 | return (&clnt_res); 115 | } 116 | -------------------------------------------------------------------------------- /libs/shmemsem/shmemsem_helper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | /* 15 | * based on Keith Gaughan - Shared Memory and Semaphores - March 22, 2003 16 | */ 17 | #include "shmemsem_helper.h" 18 | 19 | /** 20 | * Allocates a shared memory segment. 21 | * 22 | * @param n Size (in bytes) of chunk to allocate. 23 | * @return Id of shared memory chunk. 24 | */ 25 | int AllocateSharedMemory(int n) 26 | { 27 | assert(n > 0); // Idiot-proof the call. 28 | return shmget(IPC_PRIVATE, n, IPC_CREAT | SHM_R | SHM_W); 29 | } 30 | 31 | /** 32 | * Maps a shared memory segment onto our address space. 33 | * 34 | * @param id Shared memory block to map. 35 | * @return Address of mapped block. 36 | */ 37 | void* MapSharedMemory(int id) 38 | { 39 | void* addr; 40 | assert(id != 0); // Idiot-proof the call. 41 | addr = shmat(id, NULL, 0); // Attach the segment... 42 | shmctl(id, IPC_RMID, NULL); // ...and mark it destroyed. 43 | return addr; 44 | } 45 | 46 | /** 47 | * Maps a shared memory segment onto our address space. 48 | * 49 | * @param id Shared memory block to map. 50 | * @param existingAddr Adress of memory block to be shared. 51 | * @return Address of mapped block. 52 | */ 53 | void* MapExistingSharedMemory(int id, void* existingAddr) 54 | { 55 | void* addr; 56 | assert(id != 0); // Idiot-proof the call. 57 | addr = shmat(id, existingAddr, 0); // Attach the segment... 58 | shmctl(id, IPC_RMID, NULL); // ...and mark it destroyed. 59 | return addr; 60 | } 61 | 62 | /** 63 | * Detaches a shared memory segment 64 | * 65 | * @param id Shared memory block to map. 66 | * @param existingAddr Adress of memory block to be shared. 67 | * @return Address of mapped block. 68 | */ 69 | int DetachExistingSharedMemory(void* existingAddr) 70 | { 71 | int id = shmdt(existingAddr); // Detach the segment... 72 | return id; 73 | } 74 | 75 | /** 76 | * Creates a new semaphore set. 77 | * 78 | * @param n Number of semaphores in set. 79 | * @param vals Default values to start off with. 80 | * @return Id of semaphore set. 81 | */ 82 | int CreateSemaphoreSet(int n, unsigned short* vals) 83 | { 84 | union semun arg; 85 | int id; 86 | assert(n > 0); // You need at least one! 87 | assert(vals != NULL); // And they need initialising! 88 | id = semget(IPC_PRIVATE, n, SHM_R | SHM_W); 89 | arg.array = vals; 90 | //int ctl = semctl(id, 0, SETALL, arg); 91 | semctl(id, 0, SETALL, arg); 92 | return id; 93 | } 94 | 95 | /** 96 | * Frees up the given semaphore set. 97 | * 98 | * @param id Id of the semaphore group. 99 | */ 100 | void DeleteSemaphoreSet(int id) 101 | { 102 | //if (semctl(id, 0, IPC_RMID, NULL) == -1) 103 | if (semctl(id, 0, IPC_RMID, 0) == -1) 104 | { 105 | perror("Error releasing semaphore!"); 106 | exit(EXIT_FAILURE); 107 | } 108 | //std::cout << "semaphore " << id << " deleted" << std::endl; 109 | } 110 | 111 | /** 112 | * Locks a semaphore within a semaphore set. 113 | * 114 | * @param id Semaphore set it belongs to. 115 | * @param i Actual semaphore to lock. 116 | * 117 | * @note If it’s already locked, you’re put to sleep. 118 | */ 119 | void LockSemaphore(int id, int i) 120 | { 121 | struct sembuf sb; 122 | sb.sem_num = i; 123 | sb.sem_op = -1; 124 | sb.sem_flg = SEM_UNDO; 125 | semop(id, &sb, 1); 126 | } 127 | 128 | /** 129 | * Unlocks a semaphore within a semaphore set. 130 | * 131 | * @param id Semaphore set it belongs to. 132 | * @param i Actual semaphore to unlock. 133 | */ 134 | void UnlockSemaphore(int id, int i) 135 | { 136 | struct sembuf sb; 137 | sb.sem_num = i; 138 | sb.sem_op = 1; 139 | sb.sem_flg = SEM_UNDO; 140 | semop(id, &sb, 1); 141 | } 142 | -------------------------------------------------------------------------------- /cdmi-stub/imp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include "cdmi.h" 15 | 16 | using namespace std; 17 | 18 | namespace CDMi 19 | { 20 | 21 | class CMediaKeySession : public IMediaKeySession 22 | { 23 | public: 24 | CMediaKeySession(const char *sessionId); 25 | 26 | virtual ~CMediaKeySession(void); 27 | 28 | virtual void Run( 29 | const IMediaKeySessionCallback *f_piMediaKeySessionCallback); 30 | 31 | void* RunThread(int i); 32 | 33 | virtual void Update( 34 | const uint8_t *f_pbKeyMessageResponse, 35 | uint32_t f_cbKeyMessageResponse); 36 | 37 | virtual void Close(void); 38 | 39 | virtual const char *GetSessionId(void) const; 40 | 41 | virtual const char *GetKeySystem(void) const; 42 | 43 | CDMi_RESULT Init( 44 | const uint8_t *f_pbInitData, 45 | uint32_t f_cbInitData, 46 | const uint8_t *f_pbCDMData, 47 | uint32_t f_cbCDMData); 48 | 49 | // Decrypt is not an IMediaKeySession interface method therefore it can only be 50 | // accessed from code that has internal knowledge of CMediaKeySession. 51 | CDMi_RESULT Decrypt( 52 | const uint8_t *f_pbSessionKey, 53 | uint32_t f_cbSessionKey, 54 | const uint32_t *f_pdwSubSampleMapping, 55 | uint32_t f_cdwSubSampleMapping, 56 | const uint8_t *f_pbIV, 57 | uint32_t f_cbIV, 58 | const uint8_t *f_pbData, 59 | uint32_t f_cbData, 60 | uint32_t *f_pcbOpaqueClearContent, 61 | uint8_t **f_ppbOpaqueClearContent); 62 | 63 | virtual CDMi_RESULT ReleaseClearContent( 64 | const uint8_t *f_pbSessionKey, 65 | uint32_t f_cbSessionKey, 66 | const uint32_t f_cbClearContentOpaque, 67 | uint8_t *f_pbClearContentOpaque ); 68 | 69 | private: 70 | static void* _CallRunThread( 71 | void *arg); 72 | 73 | static void* _CallRunThread2( 74 | void *arg); 75 | 76 | private: 77 | const char *m_sessionId; 78 | IMediaKeySessionCallback *m_piCallback; 79 | }; 80 | 81 | class CMediaKeys : public IMediaKeys 82 | { 83 | public: 84 | CMediaKeys(); 85 | 86 | virtual ~CMediaKeys(void); 87 | 88 | virtual bool IsTypeSupported( 89 | const char *f_pwszMimeType, 90 | const char *f_pwszKeySystem) const; 91 | 92 | virtual CDMi_RESULT CreateMediaKeySession( 93 | const char *f_pwszMimeType, 94 | const uint8_t *f_pbInitData, 95 | uint32_t f_cbInitData, 96 | const uint8_t *f_pbCDMData, 97 | uint32_t f_cbCDMData, 98 | IMediaKeySession **f_ppiMediaKeySession); 99 | 100 | virtual CDMi_RESULT DestroyMediaKeySession( 101 | IMediaKeySession *f_piMediaKeySession); 102 | 103 | private: 104 | static const char * CreateSessionId(); 105 | 106 | private: 107 | static uint32_t s_sessionCnt; 108 | }; 109 | 110 | class CMediaEngineSession : public IMediaEngineSession 111 | { 112 | public: 113 | CMediaEngineSession(void); 114 | 115 | virtual ~CMediaEngineSession(void); 116 | 117 | virtual CDMi_RESULT Decrypt( 118 | uint32_t f_cdwSubSampleMapping, 119 | const uint32_t *f_pdwSubSampleMapping, 120 | uint32_t f_cbIV, 121 | const uint8_t *f_pbIV, 122 | uint32_t f_cbData, 123 | const uint8_t *f_pbData, 124 | uint32_t *f_pcbOpaqueClearContent, 125 | uint8_t **f_ppbOpaqueClearContent); 126 | 127 | virtual CDMi_RESULT ReleaseClearContent( 128 | const uint32_t f_cbClearContentOpaque, 129 | uint8_t *f_pbClearContentOpaque ); 130 | 131 | CDMi_RESULT Init( 132 | const IMediaKeySession *f_piMediaKeySession, 133 | const uint8_t *f_pbSessionKey, 134 | uint32_t f_cbSessionKey); 135 | 136 | private: 137 | IMediaKeySession *m_pMediaKeySession; 138 | 139 | uint8_t *m_pbSessionKey; 140 | uint32_t m_cbSessionKey; 141 | }; 142 | 143 | } // namespace CDMi 144 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_xdr_xdr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include "opencdm_xdr.h" 7 | 8 | bool_t 9 | xdr_rpc_request_is_type_supported (XDR *xdrs, rpc_request_is_type_supported *objp) 10 | { 11 | register int32_t *buf; 12 | 13 | if (!xdr_array (xdrs, (char **)&objp->key_system.key_system_val, (u_int *) &objp->key_system.key_system_len, ~0, 14 | sizeof (char), (xdrproc_t) xdr_char)) 15 | return FALSE; 16 | if (!xdr_array (xdrs, (char **)&objp->mime_type.mime_type_val, (u_int *) &objp->mime_type.mime_type_len, ~0, 17 | sizeof (char), (xdrproc_t) xdr_char)) 18 | return FALSE; 19 | return TRUE; 20 | } 21 | 22 | bool_t 23 | xdr_rpc_request_mediakeys (XDR *xdrs, rpc_request_mediakeys *objp) 24 | { 25 | register int32_t *buf; 26 | 27 | if (!xdr_array (xdrs, (char **)&objp->key_system.key_system_val, (u_int *) &objp->key_system.key_system_len, ~0, 28 | sizeof (char), (xdrproc_t) xdr_char)) 29 | return FALSE; 30 | return TRUE; 31 | } 32 | 33 | bool_t 34 | xdr_rpc_request_callback_info (XDR *xdrs, rpc_request_callback_info *objp) 35 | { 36 | register int32_t *buf; 37 | 38 | if (!xdr_array (xdrs, (char **)&objp->hostname.hostname_val, (u_int *) &objp->hostname.hostname_len, ~0, 39 | sizeof (char), (xdrproc_t) xdr_char)) 40 | return FALSE; 41 | if (!xdr_uint64_t (xdrs, &objp->prog_num)) 42 | return FALSE; 43 | if (!xdr_uint32_t (xdrs, &objp->prog_version)) 44 | return FALSE; 45 | return TRUE; 46 | } 47 | 48 | bool_t 49 | xdr_rpc_request_create_session (XDR *xdrs, rpc_request_create_session *objp) 50 | { 51 | register int32_t *buf; 52 | 53 | if (!xdr_array (xdrs, (char **)&objp->init_data_type.init_data_type_val, (u_int *) &objp->init_data_type.init_data_type_len, ~0, 54 | sizeof (char), (xdrproc_t) xdr_char)) 55 | return FALSE; 56 | if (!xdr_array (xdrs, (char **)&objp->init_data.init_data_val, (u_int *) &objp->init_data.init_data_len, ~0, 57 | sizeof (uint8_t), (xdrproc_t) xdr_uint8_t)) 58 | return FALSE; 59 | if (!xdr_rpc_request_callback_info (xdrs, &objp->callback_info)) 60 | return FALSE; 61 | return TRUE; 62 | } 63 | 64 | bool_t 65 | xdr_rpc_request_load_session (XDR *xdrs, rpc_request_load_session *objp) 66 | { 67 | register int32_t *buf; 68 | 69 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 70 | sizeof (char), (xdrproc_t) xdr_char)) 71 | return FALSE; 72 | return TRUE; 73 | } 74 | 75 | bool_t 76 | xdr_rpc_request_session_update (XDR *xdrs, rpc_request_session_update *objp) 77 | { 78 | register int32_t *buf; 79 | 80 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 81 | sizeof (char), (xdrproc_t) xdr_char)) 82 | return FALSE; 83 | if (!xdr_array (xdrs, (char **)&objp->key.key_val, (u_int *) &objp->key.key_len, ~0, 84 | sizeof (uint8_t), (xdrproc_t) xdr_uint8_t)) 85 | return FALSE; 86 | return TRUE; 87 | } 88 | 89 | bool_t 90 | xdr_rpc_request_session_release (XDR *xdrs, rpc_request_session_release *objp) 91 | { 92 | register int32_t *buf; 93 | 94 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 95 | sizeof (char), (xdrproc_t) xdr_char)) 96 | return FALSE; 97 | return TRUE; 98 | } 99 | 100 | bool_t 101 | xdr_rpc_request_mediaengine_data (XDR *xdrs, rpc_request_mediaengine_data *objp) 102 | { 103 | register int32_t *buf; 104 | 105 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 106 | sizeof (char), (xdrproc_t) xdr_char)) 107 | return FALSE; 108 | if (!xdr_array (xdrs, (char **)&objp->auth_data.auth_data_val, (u_int *) &objp->auth_data.auth_data_len, ~0, 109 | sizeof (uint8_t), (xdrproc_t) xdr_uint8_t)) 110 | return FALSE; 111 | if (!xdr_int32_t (xdrs, &objp->id_exchange_shmem)) 112 | return FALSE; 113 | if (!xdr_int32_t (xdrs, &objp->id_exchange_sem)) 114 | return FALSE; 115 | return TRUE; 116 | } 117 | 118 | bool_t 119 | xdr_rpc_response_generic (XDR *xdrs, rpc_response_generic *objp) 120 | { 121 | register int32_t *buf; 122 | 123 | if (!xdr_int (xdrs, &objp->platform_val)) 124 | return FALSE; 125 | return TRUE; 126 | } 127 | 128 | bool_t 129 | xdr_rpc_response_create_session (XDR *xdrs, rpc_response_create_session *objp) 130 | { 131 | register int32_t *buf; 132 | 133 | if (!xdr_int (xdrs, &objp->platform_val)) 134 | return FALSE; 135 | if (!xdr_array (xdrs, (char **)&objp->session_id.session_id_val, (u_int *) &objp->session_id.session_id_len, ~0, 136 | sizeof (char), (xdrproc_t) xdr_char)) { 137 | return FALSE; 138 | } 139 | return TRUE; 140 | } 141 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_xdr_svc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #include "opencdm_xdr.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef SIG_PF 16 | #define SIG_PF void(*)(int) 17 | #endif 18 | 19 | static void 20 | open_cdm_1(struct svc_req *rqstp, register SVCXPRT *transp) 21 | { 22 | union { 23 | rpc_request_is_type_supported rpc_open_cdm_is_type_supported_1_arg; 24 | rpc_request_mediakeys rpc_open_cdm_mediakeys_1_arg; 25 | rpc_request_create_session rpc_open_cdm_mediakeys_create_session_1_arg; 26 | rpc_request_load_session rpc_open_cdm_mediakeys_load_session_1_arg; 27 | rpc_request_session_update rpc_open_cdm_mediakeysession_update_1_arg; 28 | rpc_request_session_release rpc_open_cdm_mediakeysession_release_1_arg; 29 | rpc_request_mediaengine_data rpc_open_cdm_mediaengine_1_arg; 30 | } argument; 31 | char *result; 32 | xdrproc_t _xdr_argument, _xdr_result; 33 | char *(*local)(char *, struct svc_req *); 34 | 35 | switch (rqstp->rq_proc) { 36 | case NULLPROC: 37 | (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); 38 | return; 39 | 40 | case RPC_OPEN_CDM_IS_TYPE_SUPPORTED: 41 | _xdr_argument = (xdrproc_t) xdr_rpc_request_is_type_supported; 42 | _xdr_result = (xdrproc_t) xdr_rpc_response_generic; 43 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_is_type_supported_1_svc; 44 | break; 45 | 46 | case RPC_OPEN_CDM_MEDIAKEYS: 47 | _xdr_argument = (xdrproc_t) xdr_rpc_request_mediakeys; 48 | _xdr_result = (xdrproc_t) xdr_rpc_response_generic; 49 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_mediakeys_1_svc; 50 | break; 51 | 52 | case RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION: 53 | _xdr_argument = (xdrproc_t) xdr_rpc_request_create_session; 54 | _xdr_result = (xdrproc_t) xdr_rpc_response_create_session; 55 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_mediakeys_create_session_1_svc; 56 | break; 57 | 58 | case RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION: 59 | _xdr_argument = (xdrproc_t) xdr_rpc_request_load_session; 60 | _xdr_result = (xdrproc_t) xdr_rpc_response_generic; 61 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_mediakeys_load_session_1_svc; 62 | break; 63 | 64 | case RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE: 65 | _xdr_argument = (xdrproc_t) xdr_rpc_request_session_update; 66 | _xdr_result = (xdrproc_t) xdr_rpc_response_generic; 67 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_mediakeysession_update_1_svc; 68 | break; 69 | 70 | case RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE: 71 | _xdr_argument = (xdrproc_t) xdr_rpc_request_session_release; 72 | _xdr_result = (xdrproc_t) xdr_rpc_response_generic; 73 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_mediakeysession_release_1_svc; 74 | break; 75 | 76 | case RPC_OPEN_CDM_MEDIAENGINE: 77 | _xdr_argument = (xdrproc_t) xdr_rpc_request_mediaengine_data; 78 | _xdr_result = (xdrproc_t) xdr_rpc_response_generic; 79 | local = (char *(*)(char *, struct svc_req *)) rpc_open_cdm_mediaengine_1_svc; 80 | break; 81 | 82 | default: 83 | svcerr_noproc (transp); 84 | return; 85 | } 86 | memset ((char *)&argument, 0, sizeof (argument)); 87 | if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 88 | svcerr_decode (transp); 89 | return; 90 | } 91 | result = (*local)((char *)&argument, rqstp); 92 | if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { 93 | svcerr_systemerr (transp); 94 | } 95 | if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { 96 | fprintf (stderr, "%s", "unable to free arguments"); 97 | exit (1); 98 | } 99 | return; 100 | } 101 | 102 | int 103 | main (int argc, char **argv) 104 | { 105 | register SVCXPRT *transp; 106 | 107 | pmap_unset (OPEN_CDM, OPEN_CDM_EME_5); 108 | 109 | transp = svcudp_create(RPC_ANYSOCK); 110 | if (transp == NULL) { 111 | fprintf (stderr, "%s", "cannot create udp service."); 112 | exit(1); 113 | } 114 | if (!svc_register(transp, OPEN_CDM, OPEN_CDM_EME_5, open_cdm_1, IPPROTO_UDP)) { 115 | fprintf (stderr, "%s", "unable to register (OPEN_CDM, OPEN_CDM_EME_5, udp)."); 116 | exit(1); 117 | } 118 | 119 | transp = svctcp_create(RPC_ANYSOCK, 0, 0); 120 | if (transp == NULL) { 121 | fprintf (stderr, "%s", "cannot create tcp service."); 122 | exit(1); 123 | } 124 | if (!svc_register(transp, OPEN_CDM, OPEN_CDM_EME_5, open_cdm_1, IPPROTO_TCP)) { 125 | fprintf (stderr, "%s", "unable to register (OPEN_CDM, OPEN_CDM_EME_5, tcp)."); 126 | exit(1); 127 | } 128 | 129 | svc_run (); 130 | fprintf (stderr, "%s", "svc_run returned"); 131 | exit (1); 132 | /* NOTREACHED */ 133 | } 134 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/README.md: -------------------------------------------------------------------------------- 1 | 2 | JSMN 3 | ==== 4 | 5 | jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be 6 | easily integrated into resource-limited or embedded projects. 7 | 8 | You can find more information about JSON format at [json.org][1] 9 | 10 | Library sources are available at [bitbucket.org/zserge/jsmn][2] 11 | 12 | The web page with some information about jsmn can be found at 13 | [http://zserge.com/jsmn.html][3] 14 | 15 | Philosophy 16 | ---------- 17 | 18 | Most JSON parsers offer you a bunch of functions to load JSON data, parse it 19 | and extract any value by its name. jsmn proves that checking the correctness of 20 | every JSON packet or allocating temporary objects to store parsed JSON fields 21 | often is an overkill. 22 | 23 | JSON format itself is extremely simple, so why should we complicate it? 24 | 25 | jsmn is designed to be **robust** (it should work fine even with erroneous 26 | data), **fast** (it should parse data on the fly), **portable** (no superfluous 27 | dependencies or non-standard C extensions). An of course, **simplicity** is a 28 | key feature - simple code style, simple algorithm, simple integration into 29 | other projects. 30 | 31 | Features 32 | -------- 33 | 34 | * compatible with C89 35 | * no dependencies (even libc!) 36 | * highly portable (tested on x86/amd64, ARM, AVR) 37 | * about 200 lines of code 38 | * extremely small code footprint 39 | * API contains only 2 functions 40 | * no dynamic memory allocation 41 | * incremental single-pass parsing 42 | * library code is covered with unit-tests 43 | 44 | Design 45 | ------ 46 | 47 | The rudimentary jsmn object is a **token**. Let's consider a JSON string: 48 | 49 | '{ "name" : "Jack", "age" : 27 }' 50 | 51 | It holds the following tokens: 52 | 53 | * Object: `{ "name" : "Jack", "age" : 27}` (the whole object) 54 | * Strings: `"name"`, `"Jack"`, `"age"` (keys and some values) 55 | * Number: `27` 56 | 57 | In jsmn, tokens do not hold any data, but point to token boundaries in JSON 58 | string instead. In the example above jsmn will create tokens like: Object 59 | [0..31], String [3..7], String [12..16], String [20..23], Number [27..29]. 60 | 61 | Every jsmn token has a type, which indicates the type of corresponding JSON 62 | token. jsmn supports the following token types: 63 | 64 | * Object - a container of key-value pairs, e.g.: 65 | `{ "foo":"bar", "x":0.3 }` 66 | * Array - a sequence of values, e.g.: 67 | `[ 1, 2, 3 ]` 68 | * String - a quoted sequence of chars, e.g.: `"foo"` 69 | * Primitive - a number, a boolean (`true`, `false`) or `null` 70 | 71 | Besides start/end positions, jsmn tokens for complex types (like arrays 72 | or objects) also contain a number of child items, so you can easily follow 73 | object hierarchy. 74 | 75 | This approach provides enough information for parsing any JSON data and makes 76 | it possible to use zero-copy techniques. 77 | 78 | Install 79 | ------- 80 | 81 | To clone the repository you should have mercurial installed. Just run: 82 | 83 | $ hg clone http://bitbucket.org/zserge/jsmn jsmn 84 | 85 | Repository layout is simple: jsmn.c and jsmn.h are library files, tests are in 86 | the jsmn\_test.c, you will also find README, LICENSE and Makefile files inside. 87 | 88 | To build the library, run `make`. It is also recommended to run `make test`. 89 | Let me know, if some tests fail. 90 | 91 | If build was successful, you should get a `libjsmn.a` library. 92 | The header file you should include is called `"jsmn.h"`. 93 | 94 | API 95 | --- 96 | 97 | Token types are described by `jsmntype_t`: 98 | 99 | typedef enum { 100 | JSMN_PRIMITIVE = 0, 101 | JSMN_OBJECT = 1, 102 | JSMN_ARRAY = 2, 103 | JSMN_STRING = 3 104 | } jsmntype_t; 105 | 106 | **Note:** Unlike JSON data types, primitive tokens are not divided into 107 | numbers, booleans and null, because one can easily tell the type using the 108 | first character: 109 | 110 | * 't', 'f' - boolean 111 | * 'n' - null 112 | * '-', '0'..'9' - number 113 | 114 | Token is an object of `jsmntok_t` type: 115 | 116 | typedef struct { 117 | jsmntype_t type; // Token type 118 | int start; // Token start position 119 | int end; // Token end position 120 | int size; // Number of child (nested) tokens 121 | } jsmntok_t; 122 | 123 | **Note:** string tokens point to the first character after 124 | the opening quote and the previous symbol before final quote. This was made 125 | to simplify string extraction from JSON data. 126 | 127 | All job is done by `jsmn_parser` object. You can initialize a new parser using: 128 | 129 | jsmn_parser parser; 130 | jsmntok_t tokens[10]; 131 | 132 | jsmn_init(&parser); 133 | 134 | // js - pointer to JSON string 135 | // tokens - an array of tokens available 136 | // 10 - number of tokens available 137 | jsmn_parse(&parser, js, tokens, 10); 138 | 139 | This will create a parser, and then it tries to parse up to 10 JSON tokens from 140 | the `js` string. 141 | 142 | A non-negative reutrn value of `jsmn_parse` is the number of tokens actually 143 | used by the parser. 144 | Passing NULL instead of the tokens array would not store parsing results, but 145 | instead the function will return the value of tokens needed to parse the given 146 | string. This can be useful if you don't know yet how many tokens to allocate. 147 | 148 | If something goes wrong, you will get an error. Error will be one of these: 149 | 150 | * `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted 151 | * `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large 152 | * `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data 153 | 154 | If you get `JSON_ERROR_NOMEM`, you can re-allocate more tokens and call 155 | `jsmn_parse` once more. If you read json data from the stream, you can 156 | periodically call `jsmn_parse` and check if return value is `JSON_ERROR_PART`. 157 | You will get this error until you reach the end of JSON data. 158 | 159 | Other info 160 | ---------- 161 | 162 | This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), 163 | so feel free to integrate it in your commercial products. 164 | 165 | [1]: http://www.json.org/ 166 | [2]: https://bitbucket.org/zserge/jsmn/wiki/Home 167 | [3]: http://zserge.com/jsmn.html 168 | -------------------------------------------------------------------------------- /cdmi-stub/mediakeysession.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "imp.h" 22 | #include "json_web_key.h" 23 | #include "keypairs.h" 24 | 25 | #define DESTINATION_URL_PLACEHOLDER "http://no-valid-license-server" 26 | #define NYI_KEYSYSTEM "keysystem-placeholder" 27 | #include 28 | #include 29 | 30 | 31 | #define kDecryptionKeySize 16 32 | 33 | using namespace std; 34 | 35 | namespace CDMi { 36 | 37 | static media::KeyIdAndKeyPairs g_keys; 38 | 39 | static void hex_print(const void* pv, size_t len) 40 | { 41 | const unsigned char * p = (const unsigned char*)pv; 42 | if (NULL == pv) 43 | printf("NULL"); 44 | else 45 | { 46 | size_t i = 0; 47 | for (; i::iterator it = g_keys->begin(); 60 | it!=g_keys->end(); ++it) 61 | { 62 | result << "\"" << it->first << "\" : \"" << MEDIA_KEY_STATUS_USABLE << "\"\n"; 63 | } 64 | result << "}"; 65 | return result.str(); 66 | } 67 | 68 | CMediaKeySession::CMediaKeySession(const char *sessionId) { 69 | m_sessionId = sessionId; 70 | cout << "creating mediakeysession with id: " << m_sessionId << endl; 71 | } 72 | 73 | CMediaKeySession::~CMediaKeySession(void) {} 74 | 75 | void CMediaKeySession::Run( 76 | const IMediaKeySessionCallback *f_piMediaKeySessionCallback) { 77 | int ret; 78 | pthread_t thread; 79 | 80 | cout << "#mediakeysession.Run" << endl; 81 | 82 | if (f_piMediaKeySessionCallback != NULL) { 83 | m_piCallback = const_cast(f_piMediaKeySessionCallback); 84 | 85 | ret = pthread_create(&thread, NULL, CMediaKeySession::_CallRunThread, this); 86 | if (ret == 0) { 87 | pthread_detach(thread); 88 | } else { 89 | cout << "#mediakeysession.Run: err: could not create thread" << endl; 90 | return; 91 | } 92 | } else { 93 | cout << "#mediakeysession.Run: err: MediaKeySessionCallback NULL?" << endl; 94 | } 95 | } 96 | 97 | void* CMediaKeySession::_CallRunThread(void *arg) { 98 | return ((CMediaKeySession*)arg)->RunThread(1); 99 | } 100 | 101 | void* CMediaKeySession::_CallRunThread2(void *arg) { 102 | return ((CMediaKeySession*)arg)->RunThread(2); 103 | } 104 | 105 | void* CMediaKeySession::RunThread(int i) { 106 | cout << "#mediakeysession._RunThread" << endl; 107 | 108 | if (i == 1) { 109 | m_piCallback->OnKeyMessage(NULL, 0, const_cast(DESTINATION_URL_PLACEHOLDER)); 110 | } else { 111 | m_piCallback->OnKeyReady(); 112 | } 113 | } 114 | 115 | void CMediaKeySession::Update( 116 | const uint8_t *f_pbKeyMessageResponse, 117 | uint32_t f_cbKeyMessageResponse) { 118 | int ret; 119 | pthread_t thread; 120 | std::string keys_updated; 121 | 122 | cout << "#mediakeysession.Run" << endl; 123 | std::string key_string(reinterpret_cast(f_pbKeyMessageResponse), 124 | f_cbKeyMessageResponse); 125 | //Session type is set to "0". We keep the function signature to 126 | //match Chromium's ExtractKeysFromJWKSet(...) function 127 | media::ExtractKeysFromJWKSet(key_string, &g_keys, 0); 128 | 129 | ret = pthread_create(&thread, NULL, CMediaKeySession::_CallRunThread2, this); 130 | if (ret == 0) { 131 | pthread_detach(thread); 132 | } else { 133 | cout << "#mediakeysession.Run: err: could not create thread" << endl; 134 | return; 135 | } 136 | keys_updated = keyIdAndKeyPairsToJSON(&g_keys); 137 | m_piCallback->OnKeyStatusUpdate(keys_updated.data()); 138 | } 139 | 140 | void CMediaKeySession::Close(void) {} 141 | 142 | const char *CMediaKeySession::GetSessionId(void) const { 143 | return m_sessionId; 144 | } 145 | 146 | const char *CMediaKeySession::GetKeySystem(void) const { 147 | // TODO(fhg): 148 | return NYI_KEYSYSTEM; 149 | } 150 | 151 | CDMi_RESULT CMediaKeySession::Init( 152 | const uint8_t *f_pbInitData, 153 | uint32_t f_cbInitData, 154 | const uint8_t *f_pbCDMData, 155 | uint32_t f_cbCDMData) { 156 | return CDMi_SUCCESS; 157 | } 158 | 159 | // Decrypt is not an IMediaKeySession interface method therefore it can only be 160 | // accessed from code that has internal knowledge of CMediaKeySession. 161 | CDMi_RESULT CMediaKeySession::Decrypt( 162 | const uint8_t *f_pbSessionKey, 163 | uint32_t f_cbSessionKey, 164 | const uint32_t *f_pdwSubSampleMapping, 165 | uint32_t f_cdwSubSampleMapping, 166 | const uint8_t *f_pbIV, 167 | uint32_t f_cbIV, 168 | const uint8_t *f_pbData, 169 | uint32_t f_cbData, 170 | uint32_t *f_pcbOpaqueClearContent, 171 | uint8_t **f_ppbOpaqueClearContent) { 172 | AES_KEY aes_key; 173 | uint8_t * out; /* Faked secure buffer */ 174 | const char * key; 175 | 176 | uint8_t ivec[AES_BLOCK_SIZE] = { 0 }; 177 | uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 }; 178 | unsigned int block_offset = 0; 179 | 180 | 181 | assert(f_cbIV < AES_BLOCK_SIZE); 182 | 183 | if(f_pcbOpaqueClearContent == NULL) { 184 | cout << "ERROR: f_pcbOpaqueClearContent is NULL" << endl; 185 | return -1; 186 | } 187 | 188 | out = (uint8_t*) malloc(f_cbData * sizeof(uint8_t)); 189 | 190 | if(g_keys.size() != 1) { 191 | cout << "FIXME: We support only one key at the moment. Number keys: " << g_keys.size()<< endl; 192 | } 193 | 194 | if ( (g_keys[0].second).size() != kDecryptionKeySize) { 195 | cout << "ERROR: Wrong key size" << endl; 196 | goto fail; 197 | } 198 | 199 | key = (g_keys[0].second).data(); 200 | 201 | AES_set_encrypt_key(reinterpret_cast(key), 202 | strlen(key) * 8, &aes_key) ; 203 | 204 | memcpy(&(ivec[0]), f_pbIV, f_cbIV); 205 | 206 | AES_ctr128_encrypt(reinterpret_cast(f_pbData), out, 207 | f_cbData, &aes_key, ivec, ecount_buf, &block_offset); 208 | 209 | /* Return clear content */ 210 | *f_pcbOpaqueClearContent = f_cbData; 211 | *f_ppbOpaqueClearContent = out; 212 | 213 | return CDMi_SUCCESS; 214 | fail: 215 | free(out); 216 | return -1; 217 | } 218 | 219 | CDMi_RESULT CMediaKeySession::ReleaseClearContent( 220 | const uint8_t *f_pbSessionKey, 221 | uint32_t f_cbSessionKey, 222 | const uint32_t f_cbClearContentOpaque, 223 | uint8_t *f_pbClearContentOpaque ){ 224 | free(f_pbClearContentOpaque); 225 | } 226 | } // namespace CDMi 227 | -------------------------------------------------------------------------------- /cdmi-stub/json_web_key.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | /* Copyright 2015 Linaro Ltd 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "json_web_key.h" 25 | #include "jsmn.h" 26 | #include "keypairs.h" 27 | #define MAX_JSON_TOKENS 2048 28 | #define MAX_KEY_SIZE 2048 29 | #define MAX_KEY_ID_SIZE 2048 30 | namespace media{ 31 | 32 | /* 33 | Base64 decoder based on: base64.cpp and base64.h 34 | 35 | Copyright (C) 2004-2008 René Nyffenegger 36 | 37 | This source code is provided 'as-is', without any express or implied 38 | warranty. In no event will the author be held liable for any damages 39 | arising from the use of this software. 40 | 41 | Permission is granted to anyone to use this software for any purpose, 42 | including commercial applications, and to alter it and redistribute it 43 | freely, subject to the following restrictions: 44 | 45 | 1. The origin of this source code must not be misrepresented; you must not 46 | claim that you wrote the original source code. If you use this source code 47 | in a product, an acknowledgment in the product documentation would be 48 | appreciated but is not required. 49 | 50 | 2. Altered source versions must be plainly marked as such, and must not be 51 | misrepresented as being the original source code. 52 | 53 | 3. This notice may not be removed or altered from any source distribution. 54 | 55 | René Nyffenegger rene.nyffenegger@adp-gmbh.ch 56 | 57 | */ 58 | static const std::string base64_chars = 59 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 60 | "abcdefghijklmnopqrstuvwxyz" 61 | "0123456789+/"; 62 | 63 | static inline bool is_base64(unsigned char c) { 64 | return (isalnum(c) || (c == '+') || (c == '/')); 65 | } 66 | 67 | std::string base64_decode(std::string const& encoded_string) { 68 | int in_len = encoded_string.size(); 69 | int i = 0; 70 | int j = 0; 71 | int in_ = 0; 72 | unsigned char char_array_4[4], char_array_3[3]; 73 | std::string ret; 74 | 75 | while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 76 | char_array_4[i++] = encoded_string[in_]; in_++; 77 | if (i ==4) { 78 | for (i = 0; i <4; i++) 79 | char_array_4[i] = base64_chars.find(char_array_4[i]); 80 | 81 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 82 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 83 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 84 | 85 | for (i = 0; (i < 3); i++) 86 | ret += char_array_3[i]; 87 | i = 0; 88 | } 89 | } 90 | 91 | if (i) { 92 | for (j = i; j <4; j++) 93 | char_array_4[j] = 0; 94 | 95 | for (j = 0; j <4; j++) 96 | char_array_4[j] = base64_chars.find(char_array_4[j]); 97 | 98 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 99 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 100 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 101 | 102 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | 109 | /* Checks equality of two JSON string with a char. Returns 0 if strings 110 | * equal. */ 111 | static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { 112 | if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && 113 | strncmp(json + tok->start, s, tok->end - tok->start) == 0) { 114 | return 0; 115 | } 116 | return -1; 117 | } 118 | 119 | static void fixUpURLSafeBase64(std::string &str) 120 | { 121 | std::replace( str.begin(), str.end(), '_', '/'); 122 | std::replace( str.begin(), str.end(), '-', '+'); 123 | } 124 | 125 | static bool convertStringsToKeyPair(KeyIdAndKeyPair* pair, std::string key, 126 | std::string keyId) 127 | { 128 | size_t padding; 129 | std::string decoded_key, decoded_key_id; 130 | /* Chromium removes the padding strings from the B64 strings. We need 131 | * to append them for compatibility with the B64 parsers */ 132 | padding = keyId.length()%4; 133 | 134 | if(padding > 0) 135 | keyId.append(padding, kBase64Padding); 136 | 137 | padding = key.length()%4; 138 | if(padding > 0) 139 | key.append(padding, kBase64Padding); 140 | 141 | fixUpURLSafeBase64(key); 142 | fixUpURLSafeBase64(keyId); 143 | 144 | decoded_key = base64_decode(key); 145 | decoded_key_id = base64_decode(keyId); 146 | *pair = std::make_pair(decoded_key_id, decoded_key); 147 | return true; 148 | } 149 | 150 | bool ExtractKeysFromJWKSet(const std::string& jwk_set, 151 | KeyIdAndKeyPairs* keys, 152 | int session_type) { 153 | /*We expect max 128 tokens 154 | * FIXME: We need a different and safe JSON parser. 155 | */ 156 | jsmntok_t t[MAX_JSON_TOKENS]; 157 | jsmn_parser parser; 158 | int result; 159 | const char* jchr = &(jwk_set.c_str()[0]); 160 | 161 | std::string algorithm; 162 | std::string key; 163 | std::string keyId; 164 | jsmn_init(&parser); 165 | result = jsmn_parse(&parser, jchr, jwk_set.size(), t, sizeof(t)/sizeof(t[0])); 166 | 167 | if(result<0) { 168 | std::cout << "Failed to parse JSON" << jwk_set << std::endl; 169 | return false; 170 | } 171 | 172 | if(jsoneq(jchr, &t[1], kKeysTag)!=0) { 173 | std::cout << "Unable to parse JSON. Expected kKeyTag : " << kKeysTag << std::endl; 174 | return false; 175 | } 176 | 177 | KeyIdAndKeyPairs local_keys; 178 | /* Ignore the first 2 tokens */ 179 | for(int i = 2; i < result; i++) { 180 | if(jsoneq(jchr, &t[i], kAlgTag) == 0 && (i+1) < MAX_JSON_TOKENS) { 181 | algorithm = std::string(jchr + t[i+1].start, t[i+1].end - t[i+1].start); 182 | continue; 183 | } 184 | 185 | if(jsoneq(jchr, &t[i], kKeyTag) == 0 && (i+1) < MAX_JSON_TOKENS) { 186 | if(key.size() != 0) { 187 | std::cout << "CDMI supports only one key in JSON message. Got multiple keys." << std::endl; 188 | return false; 189 | } 190 | key = std::string(jchr + t[i+1].start, t[i+1].end - t[i+1].start); 191 | continue; 192 | } 193 | 194 | if(jsoneq(jchr, &t[i], kKeyIdTag) == 0 && (i+1) < MAX_JSON_TOKENS) { 195 | if(keyId.size() != 0) { 196 | std::cout << "CDMI supports only one keyID in JSON message. Got multiple keys." << std::endl; 197 | return false; 198 | } 199 | keyId = std::string(jchr + t[i+1].start, t[i+1].end - t[i+1].start); 200 | continue; 201 | } 202 | } 203 | KeyIdAndKeyPair keyPair; 204 | convertStringsToKeyPair(&keyPair, key, keyId); 205 | local_keys.push_back(keyPair); 206 | 207 | keys->swap(local_keys); 208 | return true; 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /rpc/gen/opencdm_xdr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Please do not edit this file. 3 | * It was generated using rpcgen. 4 | */ 5 | 6 | #ifndef _OPENCDM_XDR_H_RPCGEN 7 | #define _OPENCDM_XDR_H_RPCGEN 8 | 9 | #include 10 | 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | 17 | struct rpc_request_is_type_supported { 18 | struct { 19 | u_int key_system_len; 20 | char *key_system_val; 21 | } key_system; 22 | struct { 23 | u_int mime_type_len; 24 | char *mime_type_val; 25 | } mime_type; 26 | }; 27 | typedef struct rpc_request_is_type_supported rpc_request_is_type_supported; 28 | 29 | struct rpc_request_mediakeys { 30 | struct { 31 | u_int key_system_len; 32 | char *key_system_val; 33 | } key_system; 34 | }; 35 | typedef struct rpc_request_mediakeys rpc_request_mediakeys; 36 | 37 | struct rpc_request_callback_info { 38 | struct { 39 | u_int hostname_len; 40 | char *hostname_val; 41 | } hostname; 42 | uint64_t prog_num; 43 | uint32_t prog_version; 44 | }; 45 | typedef struct rpc_request_callback_info rpc_request_callback_info; 46 | 47 | struct rpc_request_create_session { 48 | struct { 49 | u_int init_data_type_len; 50 | char *init_data_type_val; 51 | } init_data_type; 52 | struct { 53 | u_int init_data_len; 54 | uint8_t *init_data_val; 55 | } init_data; 56 | rpc_request_callback_info callback_info; 57 | }; 58 | typedef struct rpc_request_create_session rpc_request_create_session; 59 | 60 | struct rpc_request_load_session { 61 | struct { 62 | u_int session_id_len; 63 | char *session_id_val; 64 | } session_id; 65 | }; 66 | typedef struct rpc_request_load_session rpc_request_load_session; 67 | 68 | struct rpc_request_session_update { 69 | struct { 70 | u_int session_id_len; 71 | char *session_id_val; 72 | } session_id; 73 | struct { 74 | u_int key_len; 75 | uint8_t *key_val; 76 | } key; 77 | }; 78 | typedef struct rpc_request_session_update rpc_request_session_update; 79 | 80 | struct rpc_request_session_release { 81 | struct { 82 | u_int session_id_len; 83 | char *session_id_val; 84 | } session_id; 85 | }; 86 | typedef struct rpc_request_session_release rpc_request_session_release; 87 | 88 | struct rpc_request_mediaengine_data { 89 | struct { 90 | u_int session_id_len; 91 | char *session_id_val; 92 | } session_id; 93 | struct { 94 | u_int auth_data_len; 95 | uint8_t *auth_data_val; 96 | } auth_data; 97 | int32_t id_exchange_shmem; 98 | int32_t id_exchange_sem; 99 | }; 100 | typedef struct rpc_request_mediaengine_data rpc_request_mediaengine_data; 101 | 102 | struct rpc_response_generic { 103 | int platform_val; 104 | }; 105 | typedef struct rpc_response_generic rpc_response_generic; 106 | 107 | struct rpc_response_create_session { 108 | int platform_val; 109 | struct { 110 | u_int session_id_len; 111 | char *session_id_val; 112 | } session_id; 113 | }; 114 | typedef struct rpc_response_create_session rpc_response_create_session; 115 | 116 | #define OPEN_CDM 0x61135687 117 | #define OPEN_CDM_EME_5 1 118 | 119 | #if defined(__STDC__) || defined(__cplusplus) 120 | #define RPC_OPEN_CDM_IS_TYPE_SUPPORTED 1 121 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1(rpc_request_is_type_supported *, CLIENT *); 122 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1_svc(rpc_request_is_type_supported *, struct svc_req *); 123 | #define RPC_OPEN_CDM_MEDIAKEYS 2 124 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1(rpc_request_mediakeys *, CLIENT *); 125 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1_svc(rpc_request_mediakeys *, struct svc_req *); 126 | #define RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION 3 127 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1(rpc_request_create_session *, CLIENT *); 128 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1_svc(rpc_request_create_session *, struct svc_req *); 129 | #define RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION 4 130 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1(rpc_request_load_session *, CLIENT *); 131 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1_svc(rpc_request_load_session *, struct svc_req *); 132 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE 5 133 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1(rpc_request_session_update *, CLIENT *); 134 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1_svc(rpc_request_session_update *, struct svc_req *); 135 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE 6 136 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1(rpc_request_session_release *, CLIENT *); 137 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1_svc(rpc_request_session_release *, struct svc_req *); 138 | #define RPC_OPEN_CDM_MEDIAENGINE 7 139 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1(rpc_request_mediaengine_data *, CLIENT *); 140 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1_svc(rpc_request_mediaengine_data *, struct svc_req *); 141 | extern int open_cdm_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); 142 | 143 | #else /* K&R C */ 144 | #define RPC_OPEN_CDM_IS_TYPE_SUPPORTED 1 145 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1(); 146 | extern rpc_response_generic * rpc_open_cdm_is_type_supported_1_svc(); 147 | #define RPC_OPEN_CDM_MEDIAKEYS 2 148 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1(); 149 | extern rpc_response_generic * rpc_open_cdm_mediakeys_1_svc(); 150 | #define RPC_OPEN_CDM_MEDIAKEYS_CREATE_SESSION 3 151 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1(); 152 | extern rpc_response_create_session * rpc_open_cdm_mediakeys_create_session_1_svc(); 153 | #define RPC_OPEN_CDM_MEDIAKEYS_LOAD_SESSION 4 154 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1(); 155 | extern rpc_response_generic * rpc_open_cdm_mediakeys_load_session_1_svc(); 156 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_UPDATE 5 157 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1(); 158 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_update_1_svc(); 159 | #define RPC_OPEN_CDM_MEDIAKEYSESSION_RELEASE 6 160 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1(); 161 | extern rpc_response_generic * rpc_open_cdm_mediakeysession_release_1_svc(); 162 | #define RPC_OPEN_CDM_MEDIAENGINE 7 163 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1(); 164 | extern rpc_response_generic * rpc_open_cdm_mediaengine_1_svc(); 165 | extern int open_cdm_1_freeresult (); 166 | #endif /* K&R C */ 167 | 168 | /* the xdr functions */ 169 | 170 | #if defined(__STDC__) || defined(__cplusplus) 171 | extern bool_t xdr_rpc_request_is_type_supported (XDR *, rpc_request_is_type_supported*); 172 | extern bool_t xdr_rpc_request_mediakeys (XDR *, rpc_request_mediakeys*); 173 | extern bool_t xdr_rpc_request_callback_info (XDR *, rpc_request_callback_info*); 174 | extern bool_t xdr_rpc_request_create_session (XDR *, rpc_request_create_session*); 175 | extern bool_t xdr_rpc_request_load_session (XDR *, rpc_request_load_session*); 176 | extern bool_t xdr_rpc_request_session_update (XDR *, rpc_request_session_update*); 177 | extern bool_t xdr_rpc_request_session_release (XDR *, rpc_request_session_release*); 178 | extern bool_t xdr_rpc_request_mediaengine_data (XDR *, rpc_request_mediaengine_data*); 179 | extern bool_t xdr_rpc_response_generic (XDR *, rpc_response_generic*); 180 | extern bool_t xdr_rpc_response_create_session (XDR *, rpc_response_create_session*); 181 | 182 | #else /* K&R C */ 183 | extern bool_t xdr_rpc_request_is_type_supported (); 184 | extern bool_t xdr_rpc_request_mediakeys (); 185 | extern bool_t xdr_rpc_request_callback_info (); 186 | extern bool_t xdr_rpc_request_create_session (); 187 | extern bool_t xdr_rpc_request_load_session (); 188 | extern bool_t xdr_rpc_request_session_update (); 189 | extern bool_t xdr_rpc_request_session_release (); 190 | extern bool_t xdr_rpc_request_mediaengine_data (); 191 | extern bool_t xdr_rpc_response_generic (); 192 | extern bool_t xdr_rpc_response_create_session (); 193 | 194 | #endif /* K&R C */ 195 | 196 | #ifdef __cplusplus 197 | } 198 | #endif 199 | 200 | #endif /* !_OPENCDM_XDR_H_RPCGEN */ 201 | -------------------------------------------------------------------------------- /cdmi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | // For the support of portable data types such as uint8_t. 15 | #include 16 | 17 | namespace CDMi 18 | { 19 | 20 | // EME error code to which CDMi errors are mapped. Please 21 | // refer to the EME spec for details of the errors 22 | // https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/encrypted-media.html 23 | #define MEDIA_KEYERR_UNKNOWN 1 24 | #define MEDIA_KEYERR_CLIENT 2 25 | #define MEDIA_KEYERR_SERVICE 3 26 | #define MEDIA_KEYERR_OUTPUT 4 27 | #define MEDIA_KEYERR_HARDWARECHANGE 5 28 | #define MEDIA_KEYERR_DOMAIN 6 29 | 30 | // The status code returned by CDMi APIs. 31 | typedef int32_t CDMi_RESULT; 32 | 33 | #define CDMi_SUCCESS ((CDMi_RESULT)0) 34 | #define CDMi_S_FALSE ((CDMi_RESULT)1) 35 | #define CDMi_E_OUT_OF_MEMORY ((CDMi_RESULT)0x80000002) 36 | #define CDMi_E_FAIL ((CDMi_RESULT)0x80004005) 37 | #define CDMi_E_INVALID_ARG ((CDMi_RESULT)0x80070057) 38 | 39 | #define CDMi_E_SERVER_INTERNAL_ERROR ((CDMi_RESULT)0x8004C600) 40 | #define CDMi_E_SERVER_INVALID_MESSAGE ((CDMi_RESULT)0x8004C601) 41 | #define CDMi_E_SERVER_SERVICE_SPECIFIC ((CDMi_RESULT)0x8004C604) 42 | 43 | // More CDMi status codes can be defined. In general 44 | // CDMi status codes should use the same PK error codes. 45 | 46 | #define CDMi_FAILED(Status) ((CDMi_RESULT)(Status)<0) 47 | #define CDMi_SUCCEEDED(Status) ((CDMi_RESULT)(Status) >= 0) 48 | 49 | #define ChkCDMi(expr) do { \ 50 | cr = (expr); \ 51 | if( CDMi_FAILED( cr ) ) \ 52 | { \ 53 | goto ErrorExit; \ 54 | } \ 55 | } while(FALSE) 56 | 57 | /* Media Key status required by EME */ 58 | #define MEDIA_KEY_STATUS_USABLE 0 59 | #define MEDIA_KEY_STATUS_INTERNAL_ERROR 1 60 | #define MEDIA_KEY_STATUS_EXPIRED 2 61 | #define MEDIA_KEY_STATUS_OUTPUT_NOT_ALLOWED 3 62 | #define MEDIA_KEY_STATUS_OUTPUT_DOWNSCALED 4 63 | #define MEDIA_KEY_STATUS_KEY_STATUS_PENDING 5 64 | #define MEDIA_KEY_STATUS_KEY_STATUS_MAX KEY_STATUS_PENDING 65 | 66 | // IMediaKeySessionCallback defines the callback interface to receive 67 | // events originated from MediaKeySession. 68 | class IMediaKeySessionCallback 69 | { 70 | public: 71 | virtual ~IMediaKeySessionCallback(void) {} 72 | 73 | // Event fired when a key message is successfully created. 74 | virtual void OnKeyMessage( 75 | const uint8_t *f_pbKeyMessage, //__in_bcount(f_cbKeyMessage) 76 | uint32_t f_cbKeyMessage, //__in 77 | char *f_pszUrl) = 0; //__in_z_opt 78 | 79 | // Event fired when MediaKeySession has found a usable key. 80 | virtual void OnKeyReady(void) = 0; 81 | 82 | // Event fired when MediaKeySession encounters an error. 83 | virtual void OnKeyError( 84 | int16_t f_nError, 85 | CDMi_RESULT f_crSysError) = 0; 86 | 87 | //Event fired on key status update 88 | virtual void OnKeyStatusUpdate(const char* keyMessage) = 0; 89 | }; 90 | 91 | // IMediaKeySession defines the MediaKeySession interface. 92 | class IMediaKeySession 93 | { 94 | public: 95 | virtual ~IMediaKeySession(void) {} 96 | 97 | // Kicks off the process of acquiring a key. A MediaKeySession callback is supplied 98 | // to receive notifications during the process. 99 | virtual void Run( 100 | const IMediaKeySessionCallback *f_piMediaKeySessionCallback) = 0; //__in 101 | 102 | // Process a key message response. 103 | virtual void Update( 104 | const uint8_t *f_pbKeyMessageResponse, //__in_bcount(f_cbKeyMessageResponse) 105 | uint32_t f_cbKeyMessageResponse) = 0; //__in 106 | 107 | // Explicitly release all resources associated with the MediaKeySession. 108 | virtual void Close(void) = 0; 109 | 110 | // Return the session ID of the MediaKeySession. The returned pointer 111 | // is valid as long as the associated MediaKeySession still exists. 112 | virtual const char *GetSessionId(void) const = 0; 113 | 114 | // Return the key system of the MediaKeySession. 115 | virtual const char *GetKeySystem(void) const = 0; 116 | }; 117 | 118 | // IMediaKeys defines the MediaKeys interface. 119 | class IMediaKeys 120 | { 121 | public: 122 | virtual ~IMediaKeys(void) {} 123 | 124 | // Check whether the MediaKey supports a specific mime type (optional) 125 | // and a key system. 126 | virtual bool IsTypeSupported( 127 | const char *f_pszMimeType, 128 | const char *f_pszKeySystem) const = 0; 129 | 130 | // Create a MediaKeySession using the supplied init data and CDM data. 131 | virtual CDMi_RESULT CreateMediaKeySession( 132 | const char *f_pszMimeType, 133 | const uint8_t *f_pbInitData, 134 | uint32_t f_cbInitData, 135 | const uint8_t *f_pbCDMData, 136 | uint32_t f_cbCDMData, 137 | IMediaKeySession **f_ppiMediaKeySession) = 0; 138 | 139 | // Destroy a MediaKeySession instance. 140 | virtual CDMi_RESULT DestroyMediaKeySession( 141 | IMediaKeySession *f_piMediaKeySession) = 0; 142 | }; 143 | 144 | // Global factory method that creates a MediaKeys instance. 145 | CDMi_RESULT CreateMediaKeys( 146 | IMediaKeys **f_ppiMediaKeys); //__deref_out 147 | 148 | // Global method that destroys a MediaKeys instance. 149 | CDMi_RESULT DestroyMediaKeys( 150 | IMediaKeys *f_piMediaKeys); //__in 151 | 152 | // IMediaEngineSession represents a secure channel between media engine and CDMi 153 | // for the purpose of sample decryption. 154 | // 155 | // Note: This interface below is at the prototype stage and it will undergo further design 156 | // review to be finalized. 157 | class IMediaEngineSession 158 | { 159 | public: 160 | virtual ~IMediaEngineSession(void) {} 161 | 162 | // Decrypt a content buffer: 163 | // The output can be a handle in some protected memory space usable by graphics 164 | // system, pointer to decrypted content or sample protected (encrypted) content; 165 | // in case of decrypted content it is assumed that the memory space being 166 | // operated in is secure from external activities and thus conforms 167 | // to the requirement of content being protected. Clear content has to be released 168 | // through ReleaseClearContent API. It is up to the implementer of the CDM to make 169 | // sure it satisfies all compliance and robustness rules. 170 | virtual CDMi_RESULT Decrypt( 171 | uint32_t f_cdwSubSampleMapping, 172 | const uint32_t *f_pdwSubSampleMapping, 173 | uint32_t f_cbIV, 174 | const uint8_t *f_pbIV, 175 | uint32_t f_cbData, 176 | const uint8_t *f_pbData, 177 | uint32_t *f_pcbOpaqueClearContent, 178 | uint8_t **f_ppbOpaqueClearContent) = 0; 179 | 180 | virtual CDMi_RESULT ReleaseClearContent( 181 | const uint32_t f_cbClearContentOpaque, 182 | uint8_t *f_pbClearContentOpaque ) = 0; 183 | }; 184 | 185 | // Global factory method that creates a MediaEngineSession instance. 186 | // If the returned media engine session interface is not needed then it must be released 187 | // via the call of DestroyMediaEngineSession. 188 | // if more information is necessary for instantiation of the class, 189 | // the implementation of this method can be augmented. 190 | CDMi_RESULT CreateMediaEngineSession( 191 | IMediaKeySession *f_piMediaKeySession, 192 | IMediaEngineSession **f_ppiMediaEngineSession); 193 | 194 | // Global method that destroys a MediaEngineSession instance. 195 | CDMi_RESULT DestroyMediaEngineSession( 196 | IMediaEngineSession *f_piMediaEngineSession); //__in 197 | 198 | } // namespace CDMi 199 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/jsmn.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "jsmn.h" 4 | 5 | /** 6 | * Allocates a fresh unused token from the token pull. 7 | */ 8 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 9 | jsmntok_t *tokens, size_t num_tokens) { 10 | jsmntok_t *tok; 11 | if (parser->toknext >= num_tokens) { 12 | return NULL; 13 | } 14 | tok = &tokens[parser->toknext++]; 15 | tok->start = tok->end = -1; 16 | tok->size = 0; 17 | #ifdef JSMN_PARENT_LINKS 18 | tok->parent = -1; 19 | #endif 20 | return tok; 21 | } 22 | 23 | /** 24 | * Fills token type and boundaries. 25 | */ 26 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 27 | int start, int end) { 28 | token->type = type; 29 | token->start = start; 30 | token->end = end; 31 | token->size = 0; 32 | } 33 | 34 | /** 35 | * Fills next available token with JSON primitive. 36 | */ 37 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, 38 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 39 | jsmntok_t *token; 40 | int start; 41 | 42 | start = parser->pos; 43 | 44 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 45 | switch (js[parser->pos]) { 46 | #ifndef JSMN_STRICT 47 | /* In strict mode primitive must be followed by "," or "}" or "]" */ 48 | case ':': 49 | #endif 50 | case '\t' : case '\r' : case '\n' : case ' ' : 51 | case ',' : case ']' : case '}' : 52 | goto found; 53 | } 54 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 55 | parser->pos = start; 56 | return JSMN_ERROR_INVAL; 57 | } 58 | } 59 | #ifdef JSMN_STRICT 60 | /* In strict mode primitive must be followed by a comma/object/array */ 61 | parser->pos = start; 62 | return JSMN_ERROR_PART; 63 | #endif 64 | 65 | found: 66 | if (tokens == NULL) { 67 | parser->pos--; 68 | return 0; 69 | } 70 | token = jsmn_alloc_token(parser, tokens, num_tokens); 71 | if (token == NULL) { 72 | parser->pos = start; 73 | return JSMN_ERROR_NOMEM; 74 | } 75 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 76 | #ifdef JSMN_PARENT_LINKS 77 | token->parent = parser->toksuper; 78 | #endif 79 | parser->pos--; 80 | return 0; 81 | } 82 | 83 | /** 84 | * Filsl next token with JSON string. 85 | */ 86 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 87 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 88 | jsmntok_t *token; 89 | 90 | int start = parser->pos; 91 | 92 | parser->pos++; 93 | 94 | /* Skip starting quote */ 95 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 96 | char c = js[parser->pos]; 97 | 98 | /* Quote: end of string */ 99 | if (c == '\"') { 100 | if (tokens == NULL) { 101 | return 0; 102 | } 103 | token = jsmn_alloc_token(parser, tokens, num_tokens); 104 | if (token == NULL) { 105 | parser->pos = start; 106 | return JSMN_ERROR_NOMEM; 107 | } 108 | jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); 109 | #ifdef JSMN_PARENT_LINKS 110 | token->parent = parser->toksuper; 111 | #endif 112 | return 0; 113 | } 114 | 115 | /* Backslash: Quoted symbol expected */ 116 | if (c == '\\' && parser->pos + 1 < len) { 117 | int i; 118 | parser->pos++; 119 | switch (js[parser->pos]) { 120 | /* Allowed escaped symbols */ 121 | case '\"': case '/' : case '\\' : case 'b' : 122 | case 'f' : case 'r' : case 'n' : case 't' : 123 | break; 124 | /* Allows escaped symbol \uXXXX */ 125 | case 'u': 126 | parser->pos++; 127 | for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { 128 | /* If it isn't a hex character we have an error */ 129 | if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 130 | (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 131 | (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 132 | parser->pos = start; 133 | return JSMN_ERROR_INVAL; 134 | } 135 | parser->pos++; 136 | } 137 | parser->pos--; 138 | break; 139 | /* Unexpected symbol */ 140 | default: 141 | parser->pos = start; 142 | return JSMN_ERROR_INVAL; 143 | } 144 | } 145 | } 146 | parser->pos = start; 147 | return JSMN_ERROR_PART; 148 | } 149 | 150 | /** 151 | * Parse JSON string and fill tokens. 152 | */ 153 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 154 | jsmntok_t *tokens, unsigned int num_tokens) { 155 | jsmnerr_t r; 156 | int i; 157 | jsmntok_t *token; 158 | int count = 0; 159 | 160 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 161 | char c; 162 | jsmntype_t type; 163 | 164 | c = js[parser->pos]; 165 | switch (c) { 166 | case '{': case '[': 167 | count++; 168 | if (tokens == NULL) { 169 | break; 170 | } 171 | token = jsmn_alloc_token(parser, tokens, num_tokens); 172 | if (token == NULL) 173 | return JSMN_ERROR_NOMEM; 174 | if (parser->toksuper != -1) { 175 | tokens[parser->toksuper].size++; 176 | #ifdef JSMN_PARENT_LINKS 177 | token->parent = parser->toksuper; 178 | #endif 179 | } 180 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 181 | token->start = parser->pos; 182 | parser->toksuper = parser->toknext - 1; 183 | break; 184 | case '}': case ']': 185 | if (tokens == NULL) 186 | break; 187 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 188 | #ifdef JSMN_PARENT_LINKS 189 | if (parser->toknext < 1) { 190 | return JSMN_ERROR_INVAL; 191 | } 192 | token = &tokens[parser->toknext - 1]; 193 | for (;;) { 194 | if (token->start != -1 && token->end == -1) { 195 | if (token->type != type) { 196 | return JSMN_ERROR_INVAL; 197 | } 198 | token->end = parser->pos + 1; 199 | parser->toksuper = token->parent; 200 | break; 201 | } 202 | if (token->parent == -1) { 203 | break; 204 | } 205 | token = &tokens[token->parent]; 206 | } 207 | #else 208 | for (i = parser->toknext - 1; i >= 0; i--) { 209 | token = &tokens[i]; 210 | if (token->start != -1 && token->end == -1) { 211 | if (token->type != type) { 212 | return JSMN_ERROR_INVAL; 213 | } 214 | parser->toksuper = -1; 215 | token->end = parser->pos + 1; 216 | break; 217 | } 218 | } 219 | /* Error if unmatched closing bracket */ 220 | if (i == -1) return JSMN_ERROR_INVAL; 221 | for (; i >= 0; i--) { 222 | token = &tokens[i]; 223 | if (token->start != -1 && token->end == -1) { 224 | parser->toksuper = i; 225 | break; 226 | } 227 | } 228 | #endif 229 | break; 230 | case '\"': 231 | r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 232 | if (r < 0) return r; 233 | count++; 234 | if (parser->toksuper != -1 && tokens != NULL) 235 | tokens[parser->toksuper].size++; 236 | break; 237 | case '\t' : case '\r' : case '\n' : case ' ': 238 | break; 239 | case ':': 240 | parser->toksuper = parser->toknext - 1; 241 | break; 242 | case ',': 243 | if (tokens != NULL && 244 | tokens[parser->toksuper].type != JSMN_ARRAY && 245 | tokens[parser->toksuper].type != JSMN_OBJECT) { 246 | #ifdef JSMN_PARENT_LINKS 247 | parser->toksuper = tokens[parser->toksuper].parent; 248 | #else 249 | for (i = parser->toknext - 1; i >= 0; i--) { 250 | if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 251 | if (tokens[i].start != -1 && tokens[i].end == -1) { 252 | parser->toksuper = i; 253 | break; 254 | } 255 | } 256 | } 257 | #endif 258 | } 259 | break; 260 | #ifdef JSMN_STRICT 261 | /* In strict mode primitives are: numbers and booleans */ 262 | case '-': case '0': case '1' : case '2': case '3' : case '4': 263 | case '5': case '6': case '7' : case '8': case '9': 264 | case 't': case 'f': case 'n' : 265 | /* And they must not be keys of the object */ 266 | if (tokens != NULL) { 267 | jsmntok_t *t = &tokens[parser->toksuper]; 268 | if (t->type == JSMN_OBJECT || 269 | (t->type == JSMN_STRING && t->size != 0)) { 270 | return JSMN_ERROR_INVAL; 271 | } 272 | } 273 | #else 274 | /* In non-strict mode every unquoted value is a primitive */ 275 | default: 276 | #endif 277 | r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 278 | if (r < 0) return r; 279 | count++; 280 | if (parser->toksuper != -1 && tokens != NULL) 281 | tokens[parser->toksuper].size++; 282 | break; 283 | 284 | #ifdef JSMN_STRICT 285 | /* Unexpected char in strict mode */ 286 | default: 287 | return JSMN_ERROR_INVAL; 288 | #endif 289 | } 290 | } 291 | 292 | for (i = parser->toknext - 1; i >= 0; i--) { 293 | /* Unmatched opened object or array */ 294 | if (tokens[i].start != -1 && tokens[i].end == -1) { 295 | return JSMN_ERROR_PART; 296 | } 297 | } 298 | 299 | return count; 300 | } 301 | 302 | /** 303 | * Creates a new parser based over a given buffer with an array of tokens 304 | * available. 305 | */ 306 | void jsmn_init(jsmn_parser *parser) { 307 | parser->pos = 0; 308 | parser->toknext = 0; 309 | parser->toksuper = -1; 310 | } 311 | 312 | -------------------------------------------------------------------------------- /service.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fraunhofer FOKUS 3 | * 4 | * Licensed under the MIT License (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "cdmi.h" 28 | 29 | // shared mem und semaphores stuff 30 | #include "libs/shmemsem/shmemsem_helper.h" 31 | 32 | extern "C" { 33 | #include "opencdm_xdr.h" 34 | #include "opencdm_callback.h" 35 | } 36 | 37 | using namespace CDMi; 38 | using namespace std; 39 | 40 | /* Compare operator for std::map */ 41 | struct cmp_char 42 | { 43 | bool operator()(char const *a, char const *b) 44 | { 45 | return strcmp(a, b) < 0; 46 | } 47 | }; 48 | 49 | IMediaKeys *g_pMediaKeys = NULL; 50 | 51 | map g_mediaKeySessions; 52 | 53 | // TODO(fhg): get rid of more globals 54 | vector g_mediaEngineSessions; 55 | char g_hostname[256]; 56 | u_long g_pnum; // program number for callback routine 57 | 58 | void doCallback(int, string, int, const char*); 59 | 60 | 61 | class CCallback : public IMediaKeySessionCallback { 62 | public: 63 | CCallback(const IMediaKeySession *fp_mediaKeySession) { 64 | m_mediaKeySession = const_cast(fp_mediaKeySession); 65 | } 66 | 67 | virtual ~CCallback(void) {} 68 | 69 | virtual void OnKeyMessage( 70 | const uint8_t *pbKeyMessage, 71 | uint32_t cbKeyMessage, 72 | char *f_pszUrl) { 73 | uint8_t *pbChallenge = NULL; 74 | uint32_t cbChallenge = 0; 75 | string message; 76 | 77 | message = string(f_pszUrl) + "#SPLIT#" + 78 | string(reinterpret_cast(pbChallenge), cbChallenge); 79 | 80 | cout << "key message: [" << message << "]" << endl; 81 | 82 | doCallback(ON_MESSAGE, message.c_str(), 0, m_mediaKeySession->GetSessionId()); 83 | } 84 | 85 | virtual void OnKeyReady(void) { 86 | cout << "OnKeyReady: Key is ready." << endl; 87 | doCallback(ON_READY, "", 0, m_mediaKeySession->GetSessionId()); 88 | } 89 | 90 | virtual void OnKeyError(int16_t f_nError, CDMi_RESULT error) { 91 | cout << "Key error is detected: " << error << endl; 92 | doCallback(ON_ERROR, "", error, m_mediaKeySession->GetSessionId()); 93 | } 94 | 95 | virtual void OnKeyStatusUpdate(const char* keyMessage) { 96 | doCallback(ON_KEY_STATUS_UPDATE, keyMessage, 0, m_mediaKeySession->GetSessionId()); 97 | } 98 | 99 | private: 100 | IMediaKeySession *m_mediaKeySession; 101 | }; 102 | 103 | 104 | rpc_response_generic* rpc_open_cdm_is_type_supported_1_svc( 105 | rpc_request_is_type_supported *type, struct svc_req *) { 106 | static CDMi_RESULT cr = CDMi_SUCCESS; 107 | rpc_response_generic *response = 108 | reinterpret_cast( 109 | malloc(sizeof(rpc_response_generic))); 110 | 111 | cout << "#rpc_open_cdm_is_type_supported_1_svc: " 112 | << type->key_system.key_system_val << endl; 113 | if (g_pMediaKeys) { 114 | cr = g_pMediaKeys->IsTypeSupported( 115 | reinterpret_cast(type->key_system.key_system_val), 116 | reinterpret_cast(type->mime_type.mime_type_val)); 117 | } else { 118 | cr = CDMi_S_FALSE; 119 | } 120 | 121 | response->platform_val = cr; 122 | return response; 123 | } 124 | 125 | rpc_response_generic* rpc_open_cdm_mediakeys_1_svc( 126 | rpc_request_mediakeys *keysystem, struct svc_req *) { 127 | static CDMi_RESULT cr = CDMi_S_FALSE; 128 | rpc_response_generic *response = 129 | reinterpret_cast( 130 | malloc(sizeof(rpc_response_generic))); 131 | 132 | cout << "#rpc_open_cdm_mediakeys_1_svc: " 133 | << keysystem->key_system.key_system_val << endl; 134 | cr = CreateMediaKeys(&g_pMediaKeys); 135 | 136 | response->platform_val = cr; 137 | return response; 138 | } 139 | 140 | rpc_response_create_session* rpc_open_cdm_mediakeys_create_session_1_svc( 141 | rpc_request_create_session *sessionmessage, struct svc_req *) { 142 | static CDMi_RESULT cr = CDMi_S_FALSE; 143 | rpc_response_create_session *response = 144 | reinterpret_cast( 145 | malloc(sizeof(rpc_response_create_session))); 146 | IMediaKeySessionCallback *callback = NULL; 147 | char *dst; 148 | 149 | // callback_info for info on how to rpc callback into browser 150 | cout << "#open_cdm_mediakeys_create_session_1_svc: prog num: " 151 | << sessionmessage->callback_info.prog_num << endl; 152 | g_pnum = sessionmessage->callback_info.prog_num; 153 | 154 | cout << "#open_cdm_mediakeys_create_session_1_svc init_data_val: " 155 | << &sessionmessage->init_data_type.init_data_type_val << endl; 156 | if (g_pMediaKeys) { 157 | IMediaKeySession *p_mediaKeySession = 158 | reinterpret_cast(malloc(sizeof(IMediaKeySession))); 159 | cr = g_pMediaKeys->CreateMediaKeySession( 160 | sessionmessage->init_data_type.init_data_type_val, 161 | sessionmessage->init_data.init_data_val, 162 | sessionmessage->init_data.init_data_len, 163 | NULL, 164 | 0, 165 | &p_mediaKeySession); 166 | 167 | if (cr == CDMi_SUCCESS) { 168 | const char *sid = p_mediaKeySession->GetSessionId(); 169 | uint32_t sid_size = strlen(sid); 170 | g_mediaKeySessions[sid] = p_mediaKeySession; 171 | dst = reinterpret_cast(malloc(sizeof(char) * sid_size)); 172 | strcpy(dst, sid); 173 | response->session_id.session_id_val = dst; 174 | response->session_id.session_id_len = sid_size; 175 | 176 | cout << "#open_cdm_mediakeys_create_session_1_svc: creating new CCallbback" << endl; 177 | callback = new CCallback(p_mediaKeySession); 178 | 179 | // generates challenge 180 | p_mediaKeySession->Run(callback); 181 | } 182 | } 183 | 184 | response->platform_val = cr; 185 | return response; 186 | } 187 | 188 | rpc_response_generic* rpc_open_cdm_mediakeys_load_session_1_svc( 189 | rpc_request_load_session *params, struct svc_req *) { 190 | return NULL; // TODO(fhg): NYI 191 | } 192 | 193 | rpc_response_generic* rpc_open_cdm_mediakeysession_update_1_svc( 194 | rpc_request_session_update *params, struct svc_req *) { 195 | static CDMi_RESULT cr = CDMi_SUCCESS; 196 | rpc_response_generic *response = 197 | reinterpret_cast( 198 | malloc(sizeof(rpc_response_generic))); 199 | IMediaKeySession *p_mediaKeySession; 200 | 201 | p_mediaKeySession = g_mediaKeySessions[params->session_id.session_id_val]; 202 | 203 | if (p_mediaKeySession) { 204 | cout << "update for session id: " << params->session_id.session_id_val << endl; 205 | cout << "key: [" << params->key.key_val << "]" << endl; 206 | p_mediaKeySession->Update(params->key.key_val, 207 | params->key.key_len); 208 | cr = CDMi_SUCCESS; 209 | } else { 210 | cout << "no session found for session id: " << params->session_id.session_id_val<< endl; 211 | cr = CDMi_S_FALSE; 212 | } 213 | 214 | response->platform_val = cr; 215 | return response; 216 | } 217 | 218 | rpc_response_generic* rpc_open_cdm_mediakeysession_release_1_svc( 219 | rpc_request_session_release *params, struct svc_req *) { 220 | static CDMi_RESULT cr = CDMi_SUCCESS; 221 | rpc_response_generic *response = 222 | reinterpret_cast( 223 | malloc(sizeof(rpc_response_generic))); 224 | IMediaKeySession *p_mediaKeySession; 225 | 226 | cout << "#open_cdm_mediakeysession_release_1_svc " << endl; 227 | 228 | p_mediaKeySession = g_mediaKeySessions[params->session_id.session_id_val]; 229 | 230 | if (p_mediaKeySession) { 231 | p_mediaKeySession->Close(); 232 | g_mediaKeySessions.erase(params->session_id.session_id_val); 233 | free(p_mediaKeySession); 234 | cr = CDMi_SUCCESS; 235 | } else { 236 | cr = CDMi_S_FALSE; 237 | } 238 | 239 | response->platform_val = cr; 240 | return response; 241 | } 242 | 243 | void decryptShmem(int idxMES, int idXchngSem, int idXchngShMem) { 244 | shmem_info *mesShmem; 245 | IMediaEngineSession *pMediaEngineSession = NULL; 246 | mesShmem = (shmem_info *) MapSharedMemory(idXchngShMem); 247 | 248 | cout << "#decryptShmem: " << idxMES << endl; 249 | cout << "#decryptShmem: " << idXchngSem << endl; 250 | cout << "#decryptShmem: " << idXchngShMem << endl; 251 | 252 | for (;;) { 253 | /* ***** BENCHMARK begin ***** */ 254 | timespec ts_bm_decrypt_start, ts_bm_decrypt_end; 255 | clock_gettime(CLOCK_MONOTONIC, &ts_bm_decrypt_start); 256 | /* ***** BENCHMARK end ***** */ 257 | 258 | CDMi_RESULT cr = CDMi_SUCCESS; 259 | if (g_mediaEngineSessions.size() -1 < idxMES) { 260 | cout << "decryptShmem: invalid media engine session idx: " 261 | << idxMES << endl; 262 | cr = CDMi_S_FALSE; 263 | return; 264 | } 265 | 266 | pMediaEngineSession = g_mediaEngineSessions.at(idxMES); 267 | 268 | if (pMediaEngineSession == NULL) { 269 | cout << "decryptShmem: no valid media engine session found" << endl; 270 | cr = CDMi_S_FALSE; 271 | return; 272 | } else { 273 | /* 274 | * TODO: (init, on create mes) 275 | * 1. transfer id of static info shmem (from client to cdmi) 276 | * 2. associate with mes 277 | * 278 | * TODO: 279 | * 1. get both shmems for corresponding media engine 280 | * 2. wait for access (lock) 281 | * 3. get size and shmem id from static shmem 282 | * 4. get dynamic shmem with sampledata 283 | * 5. decrypt inplace 284 | * 6. unlock both shmem 285 | * 286 | * HOWTO: reach end of loop, signaling end of segment? 287 | */ 288 | 289 | // lock own semaphore to get data 290 | LockSemaphore(idXchngSem, SEM_XCHNG_DECRYPT); 291 | 292 | if (mesShmem->idIvShMem == 0 293 | && mesShmem->idSampleShMem == 0 294 | && mesShmem->ivSize == 0 295 | && mesShmem->sampleSize == 0) { 296 | DetachExistingSharedMemory(mesShmem); 297 | 298 | UnlockSemaphore(idXchngSem, SEM_XCHNG_PULL); 299 | break; 300 | } 301 | 302 | uint8_t *mem_iv = (uint8_t *) MapSharedMemory(mesShmem->idIvShMem); 303 | uint8_t *mem_sample = (uint8_t *) MapSharedMemory(mesShmem->idSampleShMem); 304 | 305 | uint32_t *empty = new uint32_t[0]; 306 | uint32_t clear_content_size; 307 | static uint8_t* clear_content = NULL; 308 | /* FIXME: Releasing needs to be implemented using a separate 309 | * IPC call. Currently we assume that the previous decrypted clear 310 | * data is consumed when the Decrypt() called again. 311 | */ 312 | if(clear_content) 313 | pMediaEngineSession->ReleaseClearContent(clear_content_size, clear_content); 314 | 315 | cr = pMediaEngineSession->Decrypt( 316 | 0, //number of subsamples 317 | empty, //subsamples 318 | mesShmem->ivSize, 319 | mem_iv, 320 | mesShmem->sampleSize, 321 | mem_sample, 322 | &clear_content_size, 323 | &clear_content); 324 | 325 | // FIXME: opencdm uses a single buffer for passing the 326 | // encrypted and decrypted buffer. Due to this we need an 327 | // additional memcpy 328 | if(clear_content_size != mesShmem->sampleSize) 329 | cout << "Warning: returned clear sample size " << clear_content_size << 330 | "differs from encrypted " << 331 | "buffer size" << mesShmem->sampleSize << endl; 332 | 333 | memcpy(mem_sample, clear_content, MIN(mesShmem->sampleSize, clear_content_size) ); 334 | 335 | // detach all shared memories! 336 | DetachExistingSharedMemory(mem_iv); 337 | DetachExistingSharedMemory(mem_sample); 338 | 339 | // unlock that clnt knows about finished decryption 340 | UnlockSemaphore(idXchngSem, SEM_XCHNG_PULL); 341 | } 342 | } 343 | } 344 | 345 | rpc_response_generic* rpc_open_cdm_mediaengine_1_svc( 346 | rpc_request_mediaengine_data *params, struct svc_req *) { 347 | static CDMi_RESULT cr = CDMi_S_FALSE; 348 | rpc_response_generic *response = 349 | reinterpret_cast( 350 | malloc(sizeof(rpc_response_generic))); 351 | IMediaKeySession *p_mediaKeySession; 352 | IMediaEngineSession *pMediaEngineSession = NULL; 353 | 354 | cout << "#cdm_mediaenginesession_rpc_1_svc: " 355 | << params->id_exchange_shmem << " " 356 | << params->id_exchange_sem << endl; 357 | 358 | p_mediaKeySession = g_mediaKeySessions[params->session_id.session_id_val]; 359 | 360 | cr = CreateMediaEngineSession(p_mediaKeySession, 361 | &pMediaEngineSession); 362 | 363 | if (cr == CDMi_SUCCESS) { 364 | g_mediaEngineSessions.push_back(pMediaEngineSession); 365 | thread t(decryptShmem, 366 | g_mediaEngineSessions.size() - 1, 367 | params->id_exchange_sem, 368 | params->id_exchange_shmem); 369 | t.detach(); 370 | } else { 371 | cout << "MediaEngineSession create failed!" << endl; 372 | } 373 | 374 | response->platform_val = cr; 375 | return response; 376 | } 377 | 378 | void doCallback( 379 | int eventType, 380 | string message = "", 381 | int error = 0, 382 | const char *sid = NULL) { 383 | cout << "#doCallback" << endl; 384 | CLIENT *clnt; 385 | 386 | gethostname(g_hostname, sizeof(g_hostname)); 387 | 388 | cout << "#doCallback: eventType: " << eventType << endl; 389 | cout << "#doCallback: hostname: " << g_hostname << endl; 390 | cout << "#doCallback: prog num: " << g_pnum << endl; 391 | if ((clnt = clnt_create(g_hostname, g_pnum, 1, "tcp")) == NULL) { 392 | cerr << "service: doCallback: clnt_create" << endl; 393 | clnt_pcreateerror(g_hostname); 394 | exit(2); 395 | } 396 | 397 | const char *temp_message = message.c_str(); 398 | char ** msg = (char**) &temp_message; 399 | 400 | int * argp = reinterpret_cast(&error); 401 | 402 | int sid_size = strlen(sid); 403 | 404 | char *dst = new char[sid_size]; 405 | memcpy(dst, sid, sid_size); 406 | 407 | switch (eventType) { 408 | case ON_MESSAGE: 409 | rpc_cb_message km; 410 | km.session_id.session_id_len = sid_size; 411 | km.session_id.session_id_val = dst; 412 | km.destination_url = const_cast(temp_message); 413 | km.message = const_cast(temp_message); 414 | on_message_1(&km, clnt); 415 | break; 416 | 417 | case ON_READY: 418 | rpc_cb_ready kr; 419 | kr.session_id.session_id_len = sid_size; 420 | kr.session_id.session_id_val = dst; 421 | on_ready_1(&kr, clnt); 422 | break; 423 | 424 | case ON_ERROR: 425 | rpc_cb_error ke; 426 | ke.session_id.session_id_len = sid_size; 427 | ke.session_id.session_id_val = dst; 428 | on_error_1(&ke, clnt); 429 | break; 430 | 431 | case ON_KEY_STATUS_UPDATE: 432 | rpc_cb_key_status_update msg; 433 | msg.session_id.session_id_len = sid_size; 434 | msg.session_id.session_id_val = dst; 435 | msg.message = const_cast(temp_message); 436 | on_key_status_update_1(&msg, clnt); 437 | break; 438 | default: 439 | cerr << "doCallback: unknown eventType" << endl; 440 | } 441 | free(dst); 442 | } 443 | -------------------------------------------------------------------------------- /cdmi-stub/jsmn/jsmn_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static int test_passed = 0; 6 | static int test_failed = 0; 7 | 8 | /* Terminate current test with error */ 9 | #define fail() return __LINE__ 10 | 11 | /* Successfull end of the test case */ 12 | #define done() return 0 13 | 14 | /* Check single condition */ 15 | #define check(cond) do { if (!(cond)) fail(); } while (0) 16 | 17 | /* Test runner */ 18 | static void test(int (*func)(void), const char *name) { 19 | int r = func(); 20 | if (r == 0) { 21 | test_passed++; 22 | } else { 23 | test_failed++; 24 | printf("FAILED: %s (at line %d)\n", name, r); 25 | } 26 | } 27 | 28 | #define TOKEN_EQ(t, tok_start, tok_end, tok_type) \ 29 | ((t).start == tok_start \ 30 | && (t).end == tok_end \ 31 | && (t).type == (tok_type)) 32 | 33 | #define TOKEN_STRING(js, t, s) \ 34 | (strncmp(js+(t).start, s, (t).end - (t).start) == 0 \ 35 | && strlen(s) == (t).end - (t).start) 36 | 37 | #define TOKEN_PRINT(t) \ 38 | printf("start: %d, end: %d, type: %d, size: %d\n", \ 39 | (t).start, (t).end, (t).type, (t).size) 40 | 41 | #define JSMN_STRICT 42 | #include "jsmn.c" 43 | 44 | int test_empty() { 45 | const char *js; 46 | int r; 47 | jsmn_parser p; 48 | jsmntok_t t[10]; 49 | 50 | js = "{}"; 51 | jsmn_init(&p); 52 | r = jsmn_parse(&p, js, strlen(js), t, 10); 53 | check(r >= 0); 54 | check(t[0].type == JSMN_OBJECT); 55 | check(t[0].start == 0 && t[0].end == 2); 56 | 57 | js = "[]"; 58 | jsmn_init(&p); 59 | r = jsmn_parse(&p, js, strlen(js), t, 10); 60 | check(r >= 0); 61 | check(t[0].type == JSMN_ARRAY); 62 | check(t[0].start == 0 && t[0].end == 2); 63 | 64 | js = "{\"a\":[]}"; 65 | jsmn_init(&p); 66 | r = jsmn_parse(&p, js, strlen(js), t, 10); 67 | check(r >= 0); 68 | check(t[0].type == JSMN_OBJECT && t[0].start == 0 && t[0].end == 8); 69 | check(t[1].type == JSMN_STRING && t[1].start == 2 && t[1].end == 3); 70 | check(t[2].type == JSMN_ARRAY && t[2].start == 5 && t[2].end == 7); 71 | 72 | js = "[{},{}]"; 73 | jsmn_init(&p); 74 | r = jsmn_parse(&p, js, strlen(js), t, 10); 75 | check(r >= 0); 76 | check(t[0].type == JSMN_ARRAY && t[0].start == 0 && t[0].end == 7); 77 | check(t[1].type == JSMN_OBJECT && t[1].start == 1 && t[1].end == 3); 78 | check(t[2].type == JSMN_OBJECT && t[2].start == 4 && t[2].end == 6); 79 | return 0; 80 | } 81 | 82 | int test_simple() { 83 | const char *js; 84 | int r; 85 | jsmn_parser p; 86 | jsmntok_t tokens[10]; 87 | 88 | js = "{\"a\": 0}"; 89 | 90 | jsmn_init(&p); 91 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 92 | check(r >= 0); 93 | check(TOKEN_EQ(tokens[0], 0, 8, JSMN_OBJECT)); 94 | check(TOKEN_EQ(tokens[1], 2, 3, JSMN_STRING)); 95 | check(TOKEN_EQ(tokens[2], 6, 7, JSMN_PRIMITIVE)); 96 | 97 | check(TOKEN_STRING(js, tokens[0], js)); 98 | check(TOKEN_STRING(js, tokens[1], "a")); 99 | check(TOKEN_STRING(js, tokens[2], "0")); 100 | 101 | jsmn_init(&p); 102 | js = "[\"a\":{},\"b\":{}]"; 103 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 104 | check(r >= 0); 105 | 106 | jsmn_init(&p); 107 | js = "{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }"; 108 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 109 | check(r >= 0); 110 | 111 | return 0; 112 | } 113 | 114 | int test_primitive() { 115 | #ifndef JSMN_STRICT 116 | int r; 117 | jsmn_parser p; 118 | jsmntok_t tok[10]; 119 | const char *js; 120 | js = "\"boolVar\" : true"; 121 | jsmn_init(&p); 122 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 123 | check(r >= 0 && tok[0].type == JSMN_STRING 124 | && tok[1].type == JSMN_PRIMITIVE); 125 | check(TOKEN_STRING(js, tok[0], "boolVar")); 126 | check(TOKEN_STRING(js, tok[1], "true")); 127 | 128 | js = "\"boolVar\" : false"; 129 | jsmn_init(&p); 130 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 131 | check(r >= 0 && tok[0].type == JSMN_STRING 132 | && tok[1].type == JSMN_PRIMITIVE); 133 | check(TOKEN_STRING(js, tok[0], "boolVar")); 134 | check(TOKEN_STRING(js, tok[1], "false")); 135 | 136 | js = "\"intVar\" : 12345"; 137 | jsmn_init(&p); 138 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 139 | check(r >= 0 && tok[0].type == JSMN_STRING 140 | && tok[1].type == JSMN_PRIMITIVE); 141 | check(TOKEN_STRING(js, tok[0], "intVar")); 142 | check(TOKEN_STRING(js, tok[1], "12345")); 143 | 144 | js = "\"floatVar\" : 12.345"; 145 | jsmn_init(&p); 146 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 147 | check(r >= 0 && tok[0].type == JSMN_STRING 148 | && tok[1].type == JSMN_PRIMITIVE); 149 | check(TOKEN_STRING(js, tok[0], "floatVar")); 150 | check(TOKEN_STRING(js, tok[1], "12.345")); 151 | 152 | js = "\"nullVar\" : null"; 153 | jsmn_init(&p); 154 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 155 | check(r >= 0 && tok[0].type == JSMN_STRING 156 | && tok[1].type == JSMN_PRIMITIVE); 157 | check(TOKEN_STRING(js, tok[0], "nullVar")); 158 | check(TOKEN_STRING(js, tok[1], "null")); 159 | #endif 160 | return 0; 161 | } 162 | 163 | int test_string() { 164 | int r; 165 | jsmn_parser p; 166 | jsmntok_t tok[10]; 167 | const char *js; 168 | 169 | js = "\"strVar\" : \"hello world\""; 170 | jsmn_init(&p); 171 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 172 | check(r >= 0 && tok[0].type == JSMN_STRING 173 | && tok[1].type == JSMN_STRING); 174 | check(TOKEN_STRING(js, tok[0], "strVar")); 175 | check(TOKEN_STRING(js, tok[1], "hello world")); 176 | 177 | js = "\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\""; 178 | jsmn_init(&p); 179 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 180 | check(r >= 0 && tok[0].type == JSMN_STRING 181 | && tok[1].type == JSMN_STRING); 182 | check(TOKEN_STRING(js, tok[0], "strVar")); 183 | check(TOKEN_STRING(js, tok[1], "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\")); 184 | 185 | js = "\"strVar\" : \"\""; 186 | jsmn_init(&p); 187 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 188 | check(r >= 0 && tok[0].type == JSMN_STRING 189 | && tok[1].type == JSMN_STRING); 190 | check(TOKEN_STRING(js, tok[0], "strVar")); 191 | check(TOKEN_STRING(js, tok[1], "")); 192 | 193 | return 0; 194 | } 195 | 196 | int test_partial_string() { 197 | int r; 198 | jsmn_parser p; 199 | jsmntok_t tok[10]; 200 | const char *js; 201 | 202 | jsmn_init(&p); 203 | js = "\"x\": \"va"; 204 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 205 | check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING); 206 | check(TOKEN_STRING(js, tok[0], "x")); 207 | check(p.toknext == 1); 208 | 209 | jsmn_init(&p); 210 | char js_slash[9] = "\"x\": \"va\\"; 211 | r = jsmn_parse(&p, js_slash, sizeof(js_slash), tok, 10); 212 | check(r == JSMN_ERROR_PART); 213 | 214 | jsmn_init(&p); 215 | char js_unicode[10] = "\"x\": \"va\\u"; 216 | r = jsmn_parse(&p, js_unicode, sizeof(js_unicode), tok, 10); 217 | check(r == JSMN_ERROR_PART); 218 | 219 | js = "\"x\": \"valu"; 220 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 221 | check(r == JSMN_ERROR_PART && tok[0].type == JSMN_STRING); 222 | check(TOKEN_STRING(js, tok[0], "x")); 223 | check(p.toknext == 1); 224 | 225 | js = "\"x\": \"value\""; 226 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 227 | check(r >= 0 && tok[0].type == JSMN_STRING 228 | && tok[1].type == JSMN_STRING); 229 | check(TOKEN_STRING(js, tok[0], "x")); 230 | check(TOKEN_STRING(js, tok[1], "value")); 231 | 232 | js = "\"x\": \"value\", \"y\": \"value y\""; 233 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 234 | check(r >= 0 && tok[0].type == JSMN_STRING 235 | && tok[1].type == JSMN_STRING && tok[2].type == JSMN_STRING 236 | && tok[3].type == JSMN_STRING); 237 | check(TOKEN_STRING(js, tok[0], "x")); 238 | check(TOKEN_STRING(js, tok[1], "value")); 239 | check(TOKEN_STRING(js, tok[2], "y")); 240 | check(TOKEN_STRING(js, tok[3], "value y")); 241 | 242 | return 0; 243 | } 244 | 245 | int test_unquoted_keys() { 246 | #ifndef JSMN_STRICT 247 | int r; 248 | jsmn_parser p; 249 | jsmntok_t tok[10]; 250 | const char *js; 251 | 252 | jsmn_init(&p); 253 | js = "key1: \"value\"\nkey2 : 123"; 254 | 255 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 256 | check(r >= 0 && tok[0].type == JSMN_PRIMITIVE 257 | && tok[1].type == JSMN_STRING && tok[2].type == JSMN_PRIMITIVE 258 | && tok[3].type == JSMN_PRIMITIVE); 259 | check(TOKEN_STRING(js, tok[0], "key1")); 260 | check(TOKEN_STRING(js, tok[1], "value")); 261 | check(TOKEN_STRING(js, tok[2], "key2")); 262 | check(TOKEN_STRING(js, tok[3], "123")); 263 | #endif 264 | return 0; 265 | } 266 | 267 | int test_partial_array() { 268 | int r; 269 | jsmn_parser p; 270 | jsmntok_t tok[10]; 271 | const char *js; 272 | 273 | jsmn_init(&p); 274 | js = " [ 1, true, "; 275 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 276 | check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY 277 | && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE); 278 | 279 | js = " [ 1, true, [123, \"hello"; 280 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 281 | check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY 282 | && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE 283 | && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE); 284 | 285 | js = " [ 1, true, [123, \"hello\"]"; 286 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 287 | check(r == JSMN_ERROR_PART && tok[0].type == JSMN_ARRAY 288 | && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE 289 | && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE 290 | && tok[5].type == JSMN_STRING); 291 | /* check child nodes of the 2nd array */ 292 | check(tok[3].size == 2); 293 | 294 | js = " [ 1, true, [123, \"hello\"]]"; 295 | r = jsmn_parse(&p, js, strlen(js), tok, 10); 296 | check(r >= 0 && tok[0].type == JSMN_ARRAY 297 | && tok[1].type == JSMN_PRIMITIVE && tok[2].type == JSMN_PRIMITIVE 298 | && tok[3].type == JSMN_ARRAY && tok[4].type == JSMN_PRIMITIVE 299 | && tok[5].type == JSMN_STRING); 300 | check(tok[3].size == 2); 301 | check(tok[0].size == 3); 302 | return 0; 303 | } 304 | 305 | int test_array_nomem() { 306 | int i; 307 | int r; 308 | jsmn_parser p; 309 | jsmntok_t toksmall[10], toklarge[10]; 310 | const char *js; 311 | 312 | js = " [ 1, true, [123, \"hello\"]]"; 313 | 314 | for (i = 0; i < 6; i++) { 315 | jsmn_init(&p); 316 | memset(toksmall, 0, sizeof(toksmall)); 317 | memset(toklarge, 0, sizeof(toklarge)); 318 | r = jsmn_parse(&p, js, strlen(js), toksmall, i); 319 | check(r == JSMN_ERROR_NOMEM); 320 | 321 | memcpy(toklarge, toksmall, sizeof(toksmall)); 322 | 323 | r = jsmn_parse(&p, js, strlen(js), toklarge, 10); 324 | check(r >= 0); 325 | 326 | check(toklarge[0].type == JSMN_ARRAY && toklarge[0].size == 3); 327 | check(toklarge[3].type == JSMN_ARRAY && toklarge[3].size == 2); 328 | } 329 | return 0; 330 | } 331 | 332 | int test_objects_arrays() { 333 | int r; 334 | jsmn_parser p; 335 | jsmntok_t tokens[10]; 336 | const char *js; 337 | 338 | js = "[10}"; 339 | jsmn_init(&p); 340 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 341 | check(r == JSMN_ERROR_INVAL); 342 | 343 | js = "[10]"; 344 | jsmn_init(&p); 345 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 346 | check(r >= 0); 347 | 348 | js = "{\"a\": 1]"; 349 | jsmn_init(&p); 350 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 351 | check(r == JSMN_ERROR_INVAL); 352 | 353 | js = "{\"a\": 1}"; 354 | jsmn_init(&p); 355 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 356 | check(r >= 0); 357 | 358 | return 0; 359 | } 360 | 361 | int test_issue_22() { 362 | int r; 363 | jsmn_parser p; 364 | jsmntok_t tokens[128]; 365 | const char *js; 366 | 367 | js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " 368 | "\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " 369 | "\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " 370 | "\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, " 371 | "\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", " 372 | "\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", " 373 | "\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], " 374 | "\"tilewidth\":32, \"version\":1, \"width\":10 }"; 375 | jsmn_init(&p); 376 | r = jsmn_parse(&p, js, strlen(js), tokens, 128); 377 | check(r >= 0); 378 | #if 0 379 | for (i = 1; tokens[i].end < tokens[0].end; i++) { 380 | if (tokens[i].type == JSMN_STRING || tokens[i].type == JSMN_PRIMITIVE) { 381 | printf("%.*s\n", tokens[i].end - tokens[i].start, js + tokens[i].start); 382 | } else if (tokens[i].type == JSMN_ARRAY) { 383 | printf("[%d elems]\n", tokens[i].size); 384 | } else if (tokens[i].type == JSMN_OBJECT) { 385 | printf("{%d elems}\n", tokens[i].size); 386 | } else { 387 | TOKEN_PRINT(tokens[i]); 388 | } 389 | } 390 | #endif 391 | return 0; 392 | } 393 | 394 | int test_unicode_characters() { 395 | jsmn_parser p; 396 | jsmntok_t tokens[10]; 397 | const char *js; 398 | 399 | int r; 400 | js = "{\"a\":\"\\uAbcD\"}"; 401 | jsmn_init(&p); 402 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 403 | check(r >= 0); 404 | 405 | js = "{\"a\":\"str\\u0000\"}"; 406 | jsmn_init(&p); 407 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 408 | check(r >= 0); 409 | 410 | js = "{\"a\":\"\\uFFFFstr\"}"; 411 | jsmn_init(&p); 412 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 413 | check(r >= 0); 414 | 415 | js = "{\"a\":\"str\\uFFGFstr\"}"; 416 | jsmn_init(&p); 417 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 418 | check(r == JSMN_ERROR_INVAL); 419 | 420 | js = "{\"a\":\"str\\u@FfF\"}"; 421 | jsmn_init(&p); 422 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 423 | check(r == JSMN_ERROR_INVAL); 424 | 425 | js = "{\"a\":[\"\\u028\"]}"; 426 | jsmn_init(&p); 427 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 428 | check(r == JSMN_ERROR_INVAL); 429 | 430 | js = "{\"a\":[\"\\u0280\"]}"; 431 | jsmn_init(&p); 432 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 433 | check(r >= 0); 434 | 435 | return 0; 436 | } 437 | 438 | int test_input_length() { 439 | const char *js; 440 | int r; 441 | jsmn_parser p; 442 | jsmntok_t tokens[10]; 443 | 444 | js = "{\"a\": 0}garbage"; 445 | 446 | jsmn_init(&p); 447 | r = jsmn_parse(&p, js, 8, tokens, 10); 448 | check(r == 3); 449 | check(TOKEN_STRING(js, tokens[0], "{\"a\": 0}")); 450 | check(TOKEN_STRING(js, tokens[1], "a")); 451 | check(TOKEN_STRING(js, tokens[2], "0")); 452 | 453 | return 0; 454 | } 455 | 456 | int test_count() { 457 | jsmn_parser p; 458 | const char *js; 459 | 460 | js = "{}"; 461 | jsmn_init(&p); 462 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); 463 | 464 | js = "[]"; 465 | jsmn_init(&p); 466 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); 467 | 468 | js = "[[]]"; 469 | jsmn_init(&p); 470 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); 471 | 472 | js = "[[], []]"; 473 | jsmn_init(&p); 474 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); 475 | 476 | js = "[[], []]"; 477 | jsmn_init(&p); 478 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); 479 | 480 | js = "[[], [[]], [[], []]]"; 481 | jsmn_init(&p); 482 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); 483 | 484 | js = "[\"a\", [[], []]]"; 485 | jsmn_init(&p); 486 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); 487 | 488 | js = "[[], \"[], [[]]\", [[]]]"; 489 | jsmn_init(&p); 490 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); 491 | 492 | js = "[1, 2, 3]"; 493 | jsmn_init(&p); 494 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); 495 | 496 | js = "[1, 2, [3, \"a\"], null]"; 497 | jsmn_init(&p); 498 | check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); 499 | 500 | return 0; 501 | } 502 | 503 | int test_keyvalue() { 504 | const char *js; 505 | int r; 506 | jsmn_parser p; 507 | jsmntok_t tokens[10]; 508 | 509 | js = "{\"a\": 0, \"b\": \"c\"}"; 510 | 511 | jsmn_init(&p); 512 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 513 | check(r == 5); 514 | check(tokens[0].size == 2); /* two keys */ 515 | check(tokens[1].size == 1 && tokens[3].size == 1); /* one value per key */ 516 | check(tokens[2].size == 0 && tokens[4].size == 0); /* values have zero size */ 517 | 518 | js = "{\"a\"\n0}"; 519 | jsmn_init(&p); 520 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 521 | check(r == JSMN_ERROR_INVAL); 522 | 523 | js = "{\"a\", 0}"; 524 | jsmn_init(&p); 525 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 526 | check(r == JSMN_ERROR_INVAL); 527 | 528 | js = "{\"a\": {2}}"; 529 | jsmn_init(&p); 530 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 531 | check(r == JSMN_ERROR_INVAL); 532 | 533 | js = "{\"a\": {2: 3}}"; 534 | jsmn_init(&p); 535 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 536 | check(r == JSMN_ERROR_INVAL); 537 | 538 | 539 | js = "{\"a\": {\"a\": 2 3}}"; 540 | jsmn_init(&p); 541 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 542 | check(r == JSMN_ERROR_INVAL); 543 | return 0; 544 | } 545 | 546 | /** A huge redefinition of everything to include jsmn in non-script mode */ 547 | #define jsmn_init jsmn_init_nonstrict 548 | #define jsmn_parse jsmn_parse_nonstrict 549 | #define jsmn_parser jsmn_parser_nonstrict 550 | #define jsmn_alloc_token jsmn_alloc_token_nonstrict 551 | #define jsmn_fill_token jsmn_fill_token_nonstrict 552 | #define jsmn_parse_primitive jsmn_parse_primitive_nonstrict 553 | #define jsmn_parse_string jsmn_parse_string_nonstrict 554 | #define jsmntype_t jsmntype_nonstrict_t 555 | #define jsmnerr_t jsmnerr_nonstrict_t 556 | #define jsmntok_t jsmntok_nonstrict_t 557 | #define JSMN_PRIMITIVE JSMN_PRIMITIVE_NONSTRICT 558 | #define JSMN_OBJECT JSMN_OBJECT_NONSTRICT 559 | #define JSMN_ARRAY JSMN_ARRAY_NONSTRICT 560 | #define JSMN_STRING JSMN_STRING_NONSTRICT 561 | #define JSMN_ERROR_NOMEM JSMN_ERROR_NOMEM_NONSTRICT 562 | #define JSMN_ERROR_INVAL JSMN_ERROR_INVAL_NONSTRICT 563 | #define JSMN_ERROR_PART JSMN_ERROR_PART_NONSTRICT 564 | #undef __JSMN_H_ 565 | #undef JSMN_STRICT 566 | #include "jsmn.c" 567 | 568 | int test_nonstrict() { 569 | const char *js; 570 | int r; 571 | jsmn_parser p; 572 | jsmntok_t tokens[10]; 573 | 574 | js = "a: 0garbage"; 575 | 576 | jsmn_init(&p); 577 | r = jsmn_parse(&p, js, 4, tokens, 10); 578 | check(r == 2); 579 | check(TOKEN_STRING(js, tokens[0], "a")); 580 | check(TOKEN_STRING(js, tokens[1], "0")); 581 | 582 | js = "Day : 26\nMonth : Sep\n\nYear: 12"; 583 | jsmn_init(&p); 584 | r = jsmn_parse(&p, js, strlen(js), tokens, 10); 585 | check(r == 6); 586 | return 0; 587 | } 588 | 589 | int main() { 590 | test(test_empty, "general test for a empty JSON objects/arrays"); 591 | test(test_simple, "general test for a simple JSON string"); 592 | test(test_primitive, "test primitive JSON data types"); 593 | test(test_string, "test string JSON data types"); 594 | test(test_partial_string, "test partial JSON string parsing"); 595 | test(test_partial_array, "test partial array reading"); 596 | test(test_array_nomem, "test array reading with a smaller number of tokens"); 597 | test(test_unquoted_keys, "test unquoted keys (like in JavaScript)"); 598 | test(test_objects_arrays, "test objects and arrays"); 599 | test(test_unicode_characters, "test unicode characters"); 600 | test(test_input_length, "test strings that are not null-terminated"); 601 | test(test_issue_22, "test issue #22"); 602 | test(test_count, "test tokens count estimation"); 603 | test(test_nonstrict, "test for non-strict mode"); 604 | test(test_keyvalue, "test for keys/values"); 605 | printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); 606 | return 0; 607 | } 608 | 609 | --------------------------------------------------------------------------------