From 20bb84ffee609c708523c6aec448bd9aa0d00056 Mon Sep 17 00:00:00 2001 From: Fred Gylys-Colwell Date: Fri, 11 Sep 2020 13:30:58 -0700 Subject: [PATCH] Merge recent doc changes for OEMCrypto This is a cherry pick of recent changes to OEMCrypto and ODK. Most of these are part of the document migration to doxygen. See http://go/wvgerrit/106005 and its parents for code reviews. Bug: 144715340 Bug: 148232693 Bug: 167580674 Change-Id: I658f99c8117b974faed97322d61fac0f382283af --- libwvdrmengine/cdm/test/integration-test.mk | 1 + libwvdrmengine/cdm/test/unit-test.mk | 1 + ...devine_OEMCrypto_Version_Compatibility.pdf | Bin 136964 -> 139391 bytes .../oemcrypto/include/OEMCryptoCENC.h | 6141 ++++++++--------- libwvdrmengine/oemcrypto/include/level3.h | 3 + .../odk/include/OEMCryptoCENCCommon.h | 41 +- .../odk/include/core_message_deserialize.h | 22 +- .../odk/include/core_message_serialize.h | 12 +- .../include/core_message_serialize_proto.h | 14 +- .../odk/include/core_message_types.h | 29 +- libwvdrmengine/oemcrypto/odk/include/odk.h | 800 ++- .../oemcrypto/odk/include/odk_attributes.h | 14 + .../oemcrypto/odk/include/odk_structs.h | 272 +- .../oemcrypto/odk/include/odk_target.h | 12 +- .../odk/src/core_message_deserialize.cpp | 15 +- .../odk/src/core_message_serialize.cpp | 4 +- .../odk/src/core_message_serialize_proto.cpp | 17 +- libwvdrmengine/oemcrypto/odk/src/odk.c | 43 +- libwvdrmengine/oemcrypto/odk/src/odk_assert.h | 8 +- libwvdrmengine/oemcrypto/odk/src/odk_endian.h | 10 +- .../oemcrypto/odk/src/odk_overflow.c | 6 +- .../oemcrypto/odk/src/odk_overflow.h | 8 +- .../oemcrypto/odk/src/odk_serialize.c | 10 +- .../oemcrypto/odk/src/odk_serialize.h | 13 +- .../oemcrypto/odk/src/odk_structs_priv.h | 45 +- libwvdrmengine/oemcrypto/odk/src/odk_timer.c | 9 +- libwvdrmengine/oemcrypto/odk/src/odk_util.c | 6 +- libwvdrmengine/oemcrypto/odk/src/odk_util.h | 10 +- .../oemcrypto/odk/src/serialization_base.c | 33 +- .../oemcrypto/odk/src/serialization_base.h | 17 +- .../test/fuzzing/corpus_generator/README.md | 3 + .../corpus_generator/odk_corpus_generator.c | 32 +- .../odk_corpus_generator_helper.c | 6 +- .../odk_corpus_generator_helper.h | 9 +- .../odk/test/fuzzing/odk_fuzz_helper.cpp | 14 +- .../odk/test/fuzzing/odk_fuzz_helper.h | 75 +- .../odk/test/fuzzing/odk_fuzz_structs.h | 8 +- ...odk_license_response_fuzz_with_mutator.cpp | 3 +- ...rovisioning_response_fuzz_with_mutator.cpp | 4 +- ...odk_renewal_response_fuzz_with_mutator.cpp | 4 +- .../odk/test/odk_core_message_test.cpp | 37 + .../oemcrypto/odk/test/odk_fuzz.cpp | 326 - .../oemcrypto/odk/test/odk_test.cpp | 49 +- .../oemcrypto/odk/test/odk_test_helper.h | 18 +- .../oemcrypto/odk/test/odk_timer_test.cpp | 3 + .../ref/src/oemcrypto_engine_ref.cpp | 18 + .../oemcrypto/ref/src/oemcrypto_engine_ref.h | 21 +- .../oemcrypto/ref/src/oemcrypto_ref.cpp | 65 +- .../oemcrypto/ref/src/oemcrypto_session.cpp | 62 +- .../ref/src/oemcrypto_usage_table_ref.cpp | 9 +- libwvdrmengine/oemcrypto/test/common.mk | 2 + .../oemcrypto/test/fuzz_tests/README.md | 247 + .../fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc | 182 + .../test/fuzz_tests/oemcrypto_fuzz_helper.cc | 25 + .../test/fuzz_tests/oemcrypto_fuzz_helper.h | 106 + .../test/fuzz_tests/oemcrypto_fuzz_structs.h | 42 + .../oemcrypto_generate_signature.cc | 36 + .../oemcrypto_license_request_fuzz.cc | 28 + ...mcrypto_load_entitled_content_keys_fuzz.cc | 66 + .../fuzz_tests/oemcrypto_load_license_fuzz.cc | 30 + .../oemcrypto_load_provisioning_fuzz.cc | 27 + .../fuzz_tests/oemcrypto_load_renewal_fuzz.cc | 42 + .../oemcrypto_provisioning_request_fuzz.cc | 28 + .../oemcrypto_renewal_request_fuzz.cc | 27 + .../oemcrypto/test/fuzz_tests/sample_test.cc | 10 + .../test/oec_decrypt_fallback_chain.cpp | 73 +- .../test/oec_decrypt_fallback_chain.h | 11 +- .../oemcrypto/test/oec_device_features.cpp | 10 +- .../oemcrypto/test/oec_key_deriver.cpp | 6 + .../oemcrypto/test/oec_session_util.cpp | 299 +- .../oemcrypto/test/oec_session_util.h | 40 +- .../oemcrypto_corpus_generator_helper.cpp | 44 + .../test/oemcrypto_corpus_generator_helper.h | 30 + .../oemcrypto/test/oemcrypto_test.cpp | 408 +- .../oemcrypto/test/oemcrypto_test_main.cpp | 4 + 75 files changed, 5717 insertions(+), 4488 deletions(-) create mode 100644 libwvdrmengine/oemcrypto/odk/include/odk_attributes.h create mode 100644 libwvdrmengine/oemcrypto/odk/test/odk_core_message_test.cpp delete mode 100644 libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/README.md create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc create mode 100644 libwvdrmengine/oemcrypto/test/fuzz_tests/sample_test.cc create mode 100644 libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp create mode 100644 libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h diff --git a/libwvdrmengine/cdm/test/integration-test.mk b/libwvdrmengine/cdm/test/integration-test.mk index d9301caf..c3d2bf42 100644 --- a/libwvdrmengine/cdm/test/integration-test.mk +++ b/libwvdrmengine/cdm/test/integration-test.mk @@ -34,6 +34,7 @@ LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/cdm/util/test \ vendor/widevine/libwvdrmengine/oemcrypto/include \ vendor/widevine/libwvdrmengine/oemcrypto/test \ + vendor/widevine/libwvdrmengine/oemcrypto/test/fuzz_tests \ vendor/widevine/libwvdrmengine/oemcrypto/odk/include \ vendor/widevine/libwvdrmengine/oemcrypto/odk/kdo/include \ diff --git a/libwvdrmengine/cdm/test/unit-test.mk b/libwvdrmengine/cdm/test/unit-test.mk index cf51670e..29b8a417 100644 --- a/libwvdrmengine/cdm/test/unit-test.mk +++ b/libwvdrmengine/cdm/test/unit-test.mk @@ -31,6 +31,7 @@ LOCAL_C_INCLUDES := \ vendor/widevine/libwvdrmengine/cdm/util/test \ vendor/widevine/libwvdrmengine/oemcrypto/include \ vendor/widevine/libwvdrmengine/oemcrypto/test \ + vendor/widevine/libwvdrmengine/oemcrypto/test/fuzz_tests \ vendor/widevine/libwvdrmengine/oemcrypto/odk/include \ vendor/widevine/libwvdrmengine/oemcrypto/odk/kdo/include \ diff --git a/libwvdrmengine/docs/Widevine_OEMCrypto_Version_Compatibility.pdf b/libwvdrmengine/docs/Widevine_OEMCrypto_Version_Compatibility.pdf index 949fddba44f69b15605a29e2ef99e3f11ace8d68..996cc62efeed5069b806e8ac76ee521624d17aad 100644 GIT binary patch delta 45230 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

O&50|RH9^{C!UZV>TZRdQkz$H1FYR$Ya+5 zCZx8nSP^sPA?VK&txPE2R>yca)<`qvP)x?PMT-wj{wl z#nPig7nE<`w$dnpveRdWE0k$jA#m|{{Amg#l$97fU;Gw`0+TyzfqEaKJgSw5)B7v# zpwBjyfo_eQ5M5|rfA>O#;C&K|)++)(?_hlX)Hy_&{57+vm>BlP#X9cyXyl|tC%r$u zrySIaKH%qu8!7Th{9Dw()k<8Po6YqJt}l--T*#oOq-d96FZsTN#qXM!DIBh*$6gn( z0fVSYU#nSM9F-xiep$%@338|IKW-4qE^`CPL ztwMhV7LuS59VLLQ4ij|o@mCF{+S1%#Zo|k?Scm zW6RlM6s3?E70I4S9PsjWT$~4qaxvbyjkGnVEfCx-5sy%`=w3Yyuwe@@XF*)Se+qG{ z01{A3tu|};((%P{_dDzoGzh6`fqR-uKHnFojALDS#kd5RA2LBstc#+miify{UBOd9|_sswUOfU zN#Qr1Hivb8`DRyp7)1T@19xC_>@F4CVA%Et*#xx%e$ogIvdXbGYn2DMvmtkpi822mKl+YL3kPhV-=>Um*#UI3tLnbolE(EVHBTc7_Wr?#Ov(fc zGDa!Glu@kuL0E|Px628ihvpu${SZJIZ%DNAx_@nZKybP;v$e&cN(X(;uPB2bV1(}F7r#blV+HS`Agy}fKdW90mF}oXF zBml#evTv3te*;-x>SQOB$xu7YYb;!PR@JtUg3`*@y*3NlD-9OmLiSwaiyK{k9c=7! z^Ft?bUCmVu?__G|xOa7k)Ayro`43d5Jw!h4f?{`^7eK_eWvz=!BlPc&vx5~FKCcE1 zv9*j%cNqwghr5@ew(agg@5^e%=PUbT6hPvnW)dwnB<@$(KKH^weuxw;NtD()^5F! zVe|Fr)J_lh`_c)#+`Qyn32@yZ*(R+ZLJ%ainu6m2EFAxPYC}gWYP|u)N1#(U$_9M2 zP0u&mcd`5xljxKf47 zO63L1j1+D`I2sMdwAAEl@3RnFkmThm3l!DE0&IQjYZZhMo1+Ds_9D)jp)dW6n4sqSVN_UA376+%+XE$ioaWc5q> zTJ8OKKB1#0UYZkCJw*oju?hC}%6*H(4MrZP;VXe}(n6Ll{-sW9FVh+-2&sZx@eli2HcYN3z=!ouQCFMp~{xVKwHVVYARkH@=~R>0uPqIFMe(bYN1?wE0kt)vR^0VA@{Bj%%E-Jk`K> zI^PWC;W7aN9U23=>%<5rZL$Y1{@j}jdoS!nsCna2N%I}B8vCB+2;P`&L8}t6Y@Jdbi6(AjQ9xIj?8HOCBV|kL-1AoyqIAna zP8bsl9kBx11q7jx)P!e%B$@xY=6Z7pjwrR_CM1{=?J~?)!sE0`?a%EwVtHTxv=T}R z#=d9`;n7lKu2GdXFjpKSN$aw*LY$;;}~yN-gp_Ge!3Pn_w#G=tqhK z`?#XiDAtQ76HXy8G14D5fIDV61sqA=Ji89Z7rfkn&A*KkN*cJY0)h}^I=32MF?Uk`B(SS`xo-Y)+0O) zKE4Ze(8?emKorx#(_jinPs?uIcXPHl8*yYFHWd$FFdi)6Z45DbVhWU{2uii@m#rSI zs3M7N)CxubG54F->UCZeewHUOvV4$;5~DuF$3OA3sG1DnUGGrIx1pc*1}yH-1c@-g z`n1uP3N^P`wpE;F2`UF@M(&X4o)2m26I>7)n|;NV8VKQ!srr4`^(GSoH$BJn6TIau zb?3a6|V&z$??P0*#8-)po3%S=OtX^;@ z@in%hY{a9tS8Yn}pxHTV1GZ(G{&H&u%fkem4|JaNg!)42NK)e5DvQTNln*~9*@Ag^ zJbb`f6BL$r%^qnO&9lk=U=$lf_Nzb8Q1e8Q&(Vc08OZw};9FViSQ(i0e%^mFxNe~qJLcN-%2D6i*Bus^qFL@I^pWDoHs z7_w-!_4nr>1oL6?Zgmn*W%P<;Mj{Pp(Kpo#T4-tR*<&3inJ9Jf!V7B4`Y6fM!%v(n zTX(lII!fONNpJ9;1EvSGUViDskcttPTSs^lHV}A2G|K`*x>^s2uxox%9Q!r->2oAe zRoRX~N8JRZTz{=)G$nMvGs#HlvZm+6-i&PdnL-}9VjzF$W%j-V?9fXJ1aFdf+X2YC zX1%-Edb$`I1)g|Iq9H_$Bu#I4#*Yr|ZS-}rcbs*ZrT*8ofJSD_3~k{OKd$s-O5k=V zik>(o37on#fj5@pq@+K8>?aIvvv5+98l(07R+E^a_*2Ft0@lc;?1ljpYco82^ePIP zMjUO7W__0GYUPS+H`v&t?vU!f_y#1F%;p(2nO$etK}({k_^Mj2_%N1koU^hyEpupL zsRfC#)7t$_;JD2P`NwG+!FL^E^8SSYuEUXEQ5q8Rx&n7WPBr`^!%Za-^Ib00Nw=PN zPQapZ*Ah53rht`Jal?azR>SXj<%-m( z3vKtuG(`;uXNs;#TqyC&Uj&U<_T^@AW**5D77;s2GbRu8%g6 zT|_nn0K5_zZyk61e(EW4R{oz0`=iZ0w-(-#J{TYg8IqVxQphaXuLHdDr@>l;b^NqF zv5SZeT3AEOEsUW{YOBG^(%Q$TBWIK#1g(N_#GHr0?v{n+gjK~l2shGGksx^6YJ#6!^ERssWN+P>+NzlIhm zTSW=*u>@mM6B-m_Jz8ccew*ctd8=S>%cgK;w2qTheRh?;vC)P=Cw&oV@~HpYGau!2 z4}7HN9DxzsX*cJInNM`6wO=vlUW!p~S!$+_=b}JfH#VQVwp~j;hNC09EG3=46-2Yz zmXu>t4H8=?ChKI&)O}hm5!T9PpOGL?y_I)e(}~d0m#7xj+5*qT+$pqPwobrRfE&lG z0=vdgpO{^BSj8~+O}g3Mp>$O~ZAVG!++&f;VE)-ig4P})lA zBA)GEmnj`QPcrWcRD}kMJ%&hrk?EI|q372B6dbTH%0|1UMYhnZ9W2}2+^}(I)+2r_ zB^@w))UV1z(7s6p5!00En6}kRYOjP`$AI`S^ zNDn;Zmy0oJ8aBP~ZBi<_dZXF3*-m}&`GF$uR&E4UziK>8@3B9TuhXu> z7jDUwR$MR;UnP>+DYxOr(75>iB)Xit^m?W@EYv2?Y$cO%SvhpJc8lKnhR?vr%Y(pm@Qsov>g(^ouv}or?f4U8j+EY$ z-#4$?LJG=9a?3{lP8A*}9E^kR)IcsjN7lLF8o9b6(jWu_RtEOt`s*UDzX%JV0#+g; zbQ4~IIe90?ulx5}^`G>W=q~}v*+_;<;7ft7Uyoj=-W$-<%n0agygP%BqhXHVHe1ta z_F?Ex_+C>tQpz<@*6g3J14c%+|dji=0v#S+NW3aFQZFfp>WpMJsZB5=js-^FTCQfvhLbkXJ-~iex8G@7Hmzr2HZX7vdID$N{AGZ z>JJ*??=C%|0)ng1@jB6CAc*cd4HLg<_7oUQ6K*}TYVY&Y8<)lz(p2=da#NI)b?fLr z8g>oF))D6p`edD#7B3xz!fdtR8hM@Q`V-+zaUesrojcy;#J@}x^BLo$0+kCVZq0ST zzl^P61joo~bH(jf@m#TpMj`!>rb~`pPGn_DpzfgF)TUR0MyPBw$Tim<{cR0zH-hwK zE0K88zW}6if9i6Ga`B}sH@g@Gk&m)n_Uim0fHb^3@$)zbeMQVMiiPNQ2~M7L?Cu89 z3=mr4(|(bzicef_ek6oAlUjpMT5oLL;mS0TKoFx@r>p9xB6A~qhHN3~(y8l~d5BLO zVSXkAtwb90C03f5eY2_QMmb871YT{PP&IbU=Oa$h$>Qn6+6vK3Xb{01VR_%WkdjQ$ z0WRhuTaH8?N?@sFG_{nj1GlKA;(X;Ikg-_%B?iQLmknc~L(3y+IXvZcZm3=9DQY?G zF`p|2Q=GVGnJtosGXLta-n?-1k%tkIG=nnHtdNq_z(Fl(qBiJfDm+*z%NmylhSKqa zKhk5BdW{iPn4Yc51GxHMgs>?On*J15__)qhX)apqpD58*vvqMetVMHRIKHbu?%M{u zlWn(_4%#d`8|PreOtVCZ$O{4U!hWU!FQS@Q_r<5(V%o(q$6pCviix-*>t-W5@Lv&K zmU+2?gzXEUIMu{!=+?W3_x!e=HwYw&j%Xc|PhTDu_J3H=oOAXNI0j26Or((+P~kG4 zS3>o2nbA?q6fr89D(a9K%{@_BXe`<34u3oL zTDr+69pEE$<*L5rbrwpa_TG15s?K*5>2hYn#rMmrL+guFP*v1i4j2iht{%AxZHy;p zCi=Vl8DgXlH`nhDWN9fsT@?3c^jn$$Nb+Hoyb2NS>^vwx=c~Gsh6HI6z&%QJTt!X) zI~!IaWDQ=IZa)5(A>3iVhH<@umJ@8|={99eohy4r#zQX&SAb|Dj;_?D3Cdh%uiSS_ zYgb}|k@J*!@&;GKTfvr>3IzG<(D>o>29<$grW<3}*#6=Kw%;gGfghVY7B9Q@99Dii za1W-(U{FIWt8Kxsv?T^J01idcOn9K|!i!`FiC5ko+QM3C++$)OqV<4i&Ctvy< zP~totdF?txddWtUuiS&JI++CKM3ROCy`&MfaY!>bw3dV38Z;PIQswh;OhDIPZefSt z{b(|K{hl8BQ}hY?WZ|*Vr%cDaMz97@v%0#24*9=~-$}pYExXmE0{N`9JchqWr^xnq zPLx*FzP`Xut8&a4+J1w{Jmj@aI+`M5ce=`@d$+-}+9Mj|Unq?=bUO?}YxQ6ZGl<|d zRQE&*%3uqUts)J=<>(ib-(GZZl-pY5GNL?65OpvKG53LQy$Av-&$yvmWh;!gh$G-^ z7obx6DmKE^^9&acfx#BlnbV8)5ZyS0+9#LrvGzYM8-&c=v6SSFD~4Qw1}x!b*omAC z=C=+vgz%wc4xd@8m$Q1RTlY7q2IV0b;%Kp3`b@u?GL@mdMH>X0LB)AdAF;Kv=c&~R z1Zb%S!Z~cHDw?OL3L2v?8%VY|1s$-}!W8ba8FMticV~1r0?>7jJFA6*5rlDc5g+Ro z-B0Z)AeAdA&Ndm*b^4>on_(VB=rlDq;}1RM2bv`>dowqCMsG~^EM)wsakW|En1Th= zNl!BI7fVInac;1Hrz+>|zUcbj2(=+Rkb>2t+B=!-E&i=iUK|H9ikZTjgJ4Bh^SL0vUU!5N+V^HS14$NFVE)1T>yIqVI$N5f=jnvrm(%WG*B~9gr zMJDh+dl0qLv|}~s+iJOjeG3(+kMpkAMRdKR_-IxLsGi!d?+C>zL(ha(BpWLc?+s_W zbY$2W!2#%6DG0BPwrz z1swgeHtu_8>5g-{zQML8CvaSlt*)lc7J=YlT)n;7dUbFAn!tVWYH530lu$@B{L8nd zxFbYiwgCD37RutxV0`w5jSFQaOy88uWZ!+1_4Y7&6Q$QdyXo`vwp*#k5Bz<6@9=se z$NekdJhL3||GXmz3D3rqbb$z=2XHcSGKgC^IynKfnIB$3}} zUXyvJE?%N@C6OV7Xksa=a7nEQ#)zRLc-Dm4HK&fBT5_LFy)7Bb#0iEpifOwi<5@N z+Q^wlT1(&)t+1@kWv_gXfVE?D6k7 z=x=og4K63$0;3ks)*99!gjsW=X%?^b>g04{3uQz7&Tma^MWOBeQR%vT+J||mZNT?L zIRx^in(@&MhX?oSHfmGRM8xyGgEn%fVbf)G`DYVPpua>m?EC&%XH7+c*nPLTO0JHs_kvwwVu_S44bBU ziJ1Fe0p`s*=FM2;MVtUsDKf-)@BZV!IW5q~zHO=L^+3s$M?TEv#y$7G%12&vvb&$L z;M7*-&B**){Riud;O;+&^MBSy%9Pd_P#*+TqP=Ev9sUT1*FrfDF{wn8P!fw$8G4D5 zhffddX51XcGaoENh6J?_O&!m(TyWh{cxZNB0z$_~UJ>HA`fX0A$SJs?kHi$?9z9*y z?H5Si=X-ZFJ9C{biE>zF2)(`C2e00F%1FZy>zmsV0&6Le&5Mz+$Dh?lvv1@Npxg`{ zDL$*8Qc|G8*Uk2CPro@htV6$Z=PLhQw3d(>{tZ=O@-6iAyNS!6@8fNCyn^^}Sd^7- z5J1&6*yTwy#8q}`0+@^w2klG9_!rZDvS_-P$4_Lg=`3rSOQWv7t7`~IS_uk%6s7r7 znKCFY=2a(74yvxl_9~dQmMuJ;=?O!%>*D8>v}(Gzv+B1JF4ZH-9bcacgY5`%+)4FG z1m%&IdFQzIwXSr)6YaWqjU@`r3hiiov>rkzFJ5fxOo!4=rR;O~&QtJs=w zqKXjNi6>PeLnpkQ!5Kfb)Y7j9Ly!G5FM0`yEJcQX{u1RS)C*qak~%8L_bbK{e;Ysx zgeTl1EfnPtTwc@Yubo0)D4`P34UpcW0NN-YsitTa%uv74`xm;}M;e|L; zP}gWG?QwB8k1b9l28R)9vPU-?J^|yIcH#Dy3_L3XN}a5r(sWd*vY*}dxR4_9@SX}d z59Ef8RaJ{!5i?UJZmjyYSMJ}ab{QA3CG9Kf64Php}U7$2F1{Zna_l7BxVHSy8CHhcmV{SVaxrAQ#t+Q&gOqSvq`8r{ctrSdLo9P z^B+jrq<)0mNjrpd3LRRD zPEx;k4Xf?S_}E3`+eBL-=IT2L@cnvTAjD}|%{SrZH7g*nJIFXz=KTez1^3J8XsfOo zs;>1>llU-!>O<|e;$x7u&=64!wNG*(oOmR)RHQD7Zupo`u|pC!(6jp z4an}kMy2@}yIat}!Hbxmj3J!tx0w1$7{A4w80adH%3)dbYmmwHTY-iPtw?^wMd4WL z8JgpH>O7W{*vy4hUTBEi{|;)w#Io$4#J1G!R;W5oZBdMf`et$T4hBB1|Nq=m;n|pz zoX{ae0TvwwDN#Z#US@VS6GKBzMnfhh7A6A@0|OHSLqjejHdCXIN>gSgeqI(6RzprB z6BbTW78Vv$QzKJ$7Ir3MW+Ns;77k8!Llb@-1|>T?XF@h6LPkO*23ZqZGiP(c4}Dw) zWeX1zLbm@sg+e#Q^95WH3wjH18hBv}x;BNyX@`~+EiMpaFa*c>C7oLZcGm-QCPoum zY|=#WPS=s1{(i8r2kXm8jtoW+4R*xF0r<7D#b?S6kutrku#zbEIa|r4O3J@6Od*sH zj|e83$TXNpxDI!BUmO}M9t^urK0|PIXgmz78aIp5Ph1S%_cVKI>c!CHl#NomYpm3X zXX+;6?!A~dOX)sdWPBgRMqHO)a(hNPH-pOHd;>rKhW(JViUC1@z{$nQ0#8OJ_FWwQ F{{T^qX-xnC delta 42821 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 diff --git a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h index 6e71a894..874c2211 100644 --- a/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h +++ b/libwvdrmengine/oemcrypto/include/OEMCryptoCENC.h @@ -2,19 +2,133 @@ // source code may only be used and distributed under the Widevine Master // License Agreement. -/********************************************************************* - * OEMCryptoCENC.h +/** + * @mainpage OEMCrypto API * - * Reference APIs needed to support Widevine's crypto algorithms. + * OEMCrypto is the low level library implemented by the OEM to provide key and + * content protection, usually in a separate secure memory or process space. The + * term *OEMCrypto* refers to both the API described here and the library + * implementing the API. * - * See the document "WV Modular DRM Security Integration Guide for Common - * Encryption (CENC) -- version 16.3" for a description of this API. You - * can find this document in the widevine repository as - * docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v16.pdf - * Changes between different versions of this API are documented in the files - * docs/Widevine_Modular_DRM_Version_*_Delta.pdf + * For an overview of OEMCrypto functionality, please see + * [Widevine Modular DRM Security Integration Guide for Common + * Encryption](../oemcrypto) * - *********************************************************************/ + * The OEMCrypto API is divided into several sections. + * + * @defgroup initcontrol Initialization and Control API + * Initialization and set up OEMCrypto. + * + * @defgroup keyladder Crypto Key Ladder API + * The crypto key ladder is a mechanism for staging crypto keys for use by the + * hardware crypto engine. + * + * Keys are always encrypted for transmission. Before + * a key can be used, it must be decrypted (typically using the top key in the + * key ladder) and then added to the key ladder for upcoming decryption + * operations. The Crypto Key Ladder API requires the device to provide + * hardware support for AES-128 CTR and CBC modes and prevent clear keys from + * being exposed to the insecure OS. + * + * @defgroup decryption Decryption API + * Devices that implement the Key Ladder API must also support a secure decode + * or secure decode and rendering implementation. + * + * This can be done by either + * decrypting into buffers secured by hardware protections and providing these + * secured buffers to the decoder/renderer or by implementing decrypt operations + * in the decoder/renderer. + * + * In a Security Level 2 implementation where the video path is not protected, + * the audio and video streams are decrypted using OEMCrypto_DecryptCENC() and + * buffers are returned to the media player in the clear. + * + * Generic Modular DRM allows an application to encrypt, decrypt, sign and + * verify arbitrary user data using a content key. This content key is + * securely delivered from the server to the client device using the same + * factory installed root of trust as a media content keys. + * + * @defgroup factory_provision Factory Provisioning API + * Functions that are used to install the root of trust. This could be either a + * keybox or an OEM Certificate. + * + * Widevine keyboxes are used to establish a root of trust to secure content on + * a device that uses Provisioning 2.0. OEM Certificates are used to establish + * a root of trust to secure content on a device that uses Provisioning + * 3.0. Factory Provisioning a device is related to manufacturing methods. This + * section describes the API that installs the Widevine Keybox and the + * recommended methods for the OEM’s factory provisioning procedure. + * + * Starting with API version 10, devices should have two keyboxes. One is the + * production keybox which may be installed in the factory, or using + * OEMCrypto_WrapKeyboxOrOEMCert and OEMCrypto_InstallKeyboxOrOEMCert as + * described below. The second keybox is a test keybox. The test keybox is the + * same for all devices and is used for a suite of unit tests. The test keybox + * will only be used temporarily while the unit tests are running, and will not + * be used by the general public. After the unit tests have been run, and + * OEMCrypto_Terminate has been called, the production keybox should be active + * again. + * + * API functions marked as optional may be used by the OEM’s factory + * provisioning procedure and implemented in the library, but are not called + * from the Widevine DRM Plugin during normal operation. + * + * @defgroup keybox Keybox and Provisioning 2.0 API + * Functions that are needed to for a device with a keybox. + * + * The OEMCrypto API allows for a device to be initially provisioned with a + * keybox or with an OEM certificate. See the section Provisioning above. In a + * Level 1 or Level 2 implementation, only the security processor may access the + * keys in the keybox. The following functions are for devices that are + * provisioned with a keybox, i.e. Provisioning 2.0. + * + * @defgroup oem_cert OEM Certificate and Provisioning 3.0 API + * Functions that are needed to for a device with an OEM Certificate. + * + * The OEMCrypto API allows for a device to be initially provisioned with a + * keybox or with an OEM certificate. See the Provisioning above. The + * functions in this section are for devices that are provisioned with an OEM + * Certificate, i.e. Provisioning 3.0. + * + * API functions marked as optional may be used by the OEM’s factory + * provisioning procedure and implemented in the library, but are not called + * from the Widevine DRM Plugin during normal operation. + * + * @defgroup validation Validation and Feature Support API + * The OEMCrypto API is flexible enough to allow different devices to support + * different features. This section has functions that specify the level of + * support for various features. These values are reported to either the + * application or the license server. + * + * @defgroup drm_cert DRM Certificate Provisioning API + * This section of functions are used to provision the device with an DRM + * certificate. This certificate is obtained by a device in the field from a + * Google/Widevine provisioning server, or from a third party server running the + * Google/Widevine provisioning server SDK. Since the DRM certificate may be + * origin or application specific, a device may have several DRM certificates + * installed at a time. The DRM certificate is used to authenticate the device + * to a license server. In order to obtain a DRM certificate from a + * provisioning server, the device may authenticate itself using a keybox or + * using an OEM certificate. + * + * @defgroup usage_table Usage Table API + * The usage table is used to store license usage and allows a persistent + * license to be reloaded. + * + * @defgroup test_verify Test and Verification API + * Functions that are designed to help test OEMCrypto and the device. They are + * not used during normal operation. Some functions, like OEMCrypto_RemoveSRM + * should only be implemented on test devices. Other functions, like those that + * test the full decrypt data path may be supported on a production device with + * no added risk of security loss. + * + * The following functions are used just for testing and verification of + * OEMCrypto and the CDM code. + * + * @defgroup common_types Common Types + * Enumerations and structures that are used by several OEMCrypto and ODK + * functions. + */ #ifndef OEMCRYPTO_CENC_H_ #define OEMCRYPTO_CENC_H_ @@ -29,9 +143,17 @@ extern "C" { #endif +/// @addtogroup keyladder +/// @{ + +/// This is the internal session identifier. typedef uint32_t OEMCrypto_SESSION; -/* +/// @} + +/// @addtogroup decryption +/// @{ +/** * The memory referenced by OEMCrypto_SharedMemory* is safe to be placed in * shared memory. The only data that should be placed into shared * memory is the contents of input/output buffers, i.e. data that will @@ -40,70 +162,67 @@ typedef uint32_t OEMCrypto_SESSION; */ typedef uint8_t OEMCrypto_SharedMemory; -/* - * OEMCrypto_DestBufferDesc Structure - * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC and - * OEMCrypto_CopyBuffer functions. This describes the type and access - * information for the memory to receive decrypted data. - * - * The OEMCrypto API supports a range of client device architectures. - * Different architectures have different methods for acquiring and securing - * buffers that will hold portions of the audio or video stream after - * decryption. Three basic strategies are recognized for handling decrypted - * stream data: - * - * 1. Return the decrypted data in the clear into normal user memory - * (ClearBuffer). The caller uses normal memory allocation methods to - * acquire a buffer, and supplies the memory address of the buffer in - * the descriptor. - * 2. Place the decrypted data into protected memory (SecureBuffer). The - * caller uses a platform-specific method to acquire the protected - * buffer and a user-memory handle that references it. The handle is - * supplied to the decrypt call in the descriptor. If the buffer is - * filled with several OEMCrypto calls, the same handle will be used, - * and the offset will be incremented to indicate where the next write - * should take place. - * 3. Place the decrypted data directly into the audio or video decoder - * fifo (Direct). The caller will use platform-specific methods to - * initialize the fifo and the decoders. The decrypted stream data is - * not accessible to the caller. This is used on some platforms only. - * - * Fields: - * [in] type: A tag that indicates which variant of the union is valid for - * this instance of the structure. - * [variant] clear: This variant is valid when the type is - * OEMCrypto_BufferType_Clear. This OEMCrypto_DestBufferDesc indicates output - * should be written to a clear buffer. - * [in] address: A pointer to the address in memory to begin writing output. - * [in] address_length: The length of the buffer that is available to contain - * output. - * [variant] secure: This variant is valid when the type is - * OEMCrypto_BufferType_Secure. This OEMCrypto_DestBufferDesc indicates - * output should be written to a secure buffer. The decrypted output must - * never leave the secure area until it is output from the device. - * [in] handle: An opaque handle to a secure buffer. The meaning of this - * handle is platform-specific. - * [in] handle_length: The length of the data contained in the secure buffer. - * [in] offset: An offset indicating where in the secure buffer to start - * writing data. - * [variant] direct: This variant is valid when the type is - * OEMCrypto_BufferType_Direct. This OEMCrypto_DestBufferDesc indicates - * output should be written directly to the decoder. - * [in] is_video: A flag indicating if the data is video and should be sent - * to the video decoder. If this is false, the data can be assumed to be - * audio and sent to the audio decoder. - * - * Version: - * This struct changed in API version 16. +/** Specifies destination buffer type. */ typedef enum OEMCryptoBufferType { OEMCrypto_BufferType_Clear, OEMCrypto_BufferType_Secure, - OEMCrypto_BufferType_Direct + OEMCrypto_BufferType_Direct, + OEMCrypto_BufferType_MaxValue = OEMCrypto_BufferType_Direct, } OEMCryptoBufferType; +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC and + * OEMCrypto_CopyBuffer functions. This describes the type and access + * information for the memory to receive decrypted data. + * + * The OEMCrypto API supports a range of client device architectures. Different + * architectures have different methods for acquiring and securing buffers that + * will hold portions of the audio or video stream after decryption. Three basic + * strategies are recognized for handling decrypted stream data: + * + * 1. Return the decrypted data in the clear into normal user memory + * (ClearBuffer). The caller uses normal memory allocation methods to + * acquire a buffer, and supplies the memory address of the buffer in + * the descriptor. + * 2. Place the decrypted data into protected memory (SecureBuffer). The + * caller uses a platform-specific method to acquire the protected + * buffer and a user-memory handle that references it. The handle is + * supplied to the decrypt call in the descriptor. If the buffer is + * filled with several OEMCrypto calls, the same handle will be used, + * and the offset will be incremented to indicate where the next write + * should take place. + * 3. Place the decrypted data directly into the audio or video decoder + * fifo (Direct). The caller will use platform-specific methods to + * initialize the fifo and the decoders. The decrypted stream data is + * not accessible to the caller. This is used on some platforms only. + * + * @param[in] type: A tag that indicates which variant of the union is valid for + * this instance of the structure. [variant] clear: This variant is valid + * when the type is OEMCrypto_BufferType_Clear. This OEMCrypto_DestBufferDesc + * indicates output should be written to a clear buffer. + * @param[in] address: A pointer to the address in memory to begin writing + * output. + * @param[in] address_length: The length of the buffer that is available to + * contain output. [variant] secure: This variant is valid when the type is + * OEMCrypto_BufferType_Secure. This OEMCrypto_DestBufferDesc indicates + * output should be written to a secure buffer. The decrypted output must + * never leave the secure area until it is output from the device. + * @param[in] handle: An opaque handle to a secure buffer. The meaning of this + * handle is platform-specific. + * @param[in] handle_length: The length of the data contained in the secure + * buffer. + * @param[in] offset: An offset indicating where in the secure buffer to start + * writing data. [variant] direct: This variant is valid when the type is + * OEMCrypto_BufferType_Direct. This OEMCrypto_DestBufferDesc indicates + * output should be written directly to the decoder. + * @param[in] is_video: A flag indicating if the data is video and should be + * sent to the video decoder. If this is false, the data can be assumed to be + * audio and sent to the audio decoder. + * + * @version + * This struct changed in API version 16. + */ typedef struct { OEMCryptoBufferType type; union { @@ -122,20 +241,17 @@ typedef struct { } buffer; } OEMCrypto_DestBufferDesc; -/* - * OEMCrypto_InputOutputPair Structure +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * @param[in] input_data: An unaligned pointer to this sample from the stream. + * @param[in] input_data_length: The length of this sample in the stream, in + * bytes. + * @param[in] output_descriptor: A caller-owned descriptor that specifies the + * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc for + * details. * - * Fields: - * [in] input_data: An unaligned pointer to this sample from the stream. - * [in] input_data_length: The length of this sample in the stream, in bytes. - * [in] output_descriptor: A caller-owned descriptor that specifies the - * handling of the decrypted byte stream. See OEMCrypto_DestbufferDesc for - * details. - * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -144,39 +260,35 @@ typedef struct { OEMCrypto_DestBufferDesc output_descriptor; // destination for clear data. } OEMCrypto_InputOutputPair; -/* - * OEMCrypto_SubSampleDescription Structure +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC + * function. In the DASH specification, a sample is composed of multiple + * samples, and each subsample is composed of two regions. The first region is + * clear unprotected data. We also call this clear data or unencrypted + * data. Immediately following the clear region is the protected region. The + * protected region is encrypted or encrypted with a pattern. The pattern and + * number of bytes that are encrypted in the protected region is discussed in + * this document when we talk about the function OEMCryptoDecryptCENC. For + * historic reasons, this document also calls the protected region the encrypted + * region. * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC - * function. In the DASH specification, a sample is composed of multiple - * samples, and each subsample is composed of two regions. The first region - * is clear unprotected data. We also call this clear data or unencrypted - * data. Immediately following the clear region is the protected region. The - * protected region is encrypted or encrypted with a pattern. The pattern and - * number of bytes that are encrypted in the protected region is discussed in - * this document when we talk about the function OEMCryptoDecryptCENC. For - * historic reasons, this document also calls the protected region the - * encrypted region. + * @param[in] num_bytes_clear: The number of unprotected bytes in this + * subsample. The clear bytes come before the encrypted bytes. + * @param[in] num_bytes_encrypted: The number of protected bytes in this + * subsample. The protected bytes come after the clear bytes. + * @param[in] subsample_flags: bitwise flags indicating if this is the first, + * middle, or last subsample in a sample. 1 = first subsample, 2 = last + * subsample, 3 = both first and last subsample, 0 = neither first nor last + * subsample. + * @param[in] block_offset: This will only be non-zero for the 'cenc' scheme. + * If it is non-zero, the decryption block boundary is different from the + * start of the data. block_offset should be subtracted from data to compute + * the starting address of the first decrypted block. The bytes between the + * decryption block start address and data are discarded after decryption. It + * does not adjust the beginning of the source or destination data. This + * parameter satisfies 0 <= block_offset < 16. * - * Fields: - * [in] num_bytes_clear: The number of unprotected bytes in this subsample. - * The clear bytes come before the encrypted bytes. - * [in] num_bytes_encrypted: The number of protected bytes in this subsample. - * The protected bytes come after the clear bytes. - * [in] subsample_flags: bitwise flags indicating if this is the first, - * middle, or last subsample in a sample. 1 = first subsample, 2 = last - * subsample, 3 = both first and last subsample, 0 = neither first nor last - * subsample. - * [in] block_offset: This will only be non-zero for the 'cenc' scheme. If it - * is non-zero, the decryption block boundary is different from the start of - * the data. block_offset should be subtracted from data to compute the - * starting address of the first decrypted block. The bytes between the - * decryption block start address and data are discarded after decryption. It - * does not adjust the beginning of the source or destination data. This - * parameter satisfies 0 <= block_offset < 16. - * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -189,23 +301,20 @@ typedef struct { #define OEMCrypto_FirstSubsample 1 #define OEMCrypto_LastSubsample 2 -/* - * OEMCrypto_SampleDescription Structure +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC function. + * @param[in] buffers: A structure containing information about the input and + * output buffers. + * @param[in] iv: A 16-byte array containing the IV for the initial subsample of + * the sample. + * @param[in] subsamples: A caller-owned array of OEMCrypto_SubSampleDescription + * structures. Each entry in this array describes one subsample in the + * sample. + * @param[in] subsamples_length: The length of the array pointed to by the + * subsamples parameter. * - * Fields: - * [in] buffers: A structure containing information about the input and - * output buffers. - * [in] iv: A 16-byte array containing the IV for the initial subsample of - * the sample. - * [in] subsamples: A caller-owned array of OEMCrypto_SubSampleDescription - * structures. Each entry in this array describes one subsample in the sample. - * [in] subsamples_length: The length of the array pointed to by the - * subsamples parameter. - * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -215,17 +324,14 @@ typedef struct { size_t subsamples_length; // the number of subsamples in the sample. } OEMCrypto_SampleDescription; -/* - * OEMCrypto_CENCEncryptPatternDesc Structure - * - * Description: - * This structure is used as parameters in the OEMCrypto_DecryptCENC function. +/** + * This structure is used as parameters in the OEMCrypto_DecryptCENC function. * * Fields: - * [in] encrypt: The number of 16-byte crypto blocks to encrypt. - * [in] skip: The number of 16-byte crypto blocks to leave in the clear. + * @param[in] encrypt: The number of 16-byte crypto blocks to encrypt. + * @param[in] skip: The number of 16-byte crypto blocks to leave in the clear. * - * Version: + * @version * This struct changed in API version 16. */ typedef struct { @@ -233,26 +339,31 @@ typedef struct { size_t skip; // number of 16 byte blocks to leave in clear. } OEMCrypto_CENCEncryptPatternDesc; -/** OEMCryptoCipherMode is used in SelectKey to prepare a key for either CTR +/** + * OEMCryptoCipherMode is used in SelectKey to prepare a key for either CTR * decryption or CBC decryption. */ typedef enum OEMCryptoCipherMode { OEMCrypto_CipherMode_CTR, OEMCrypto_CipherMode_CBC, + OEMCrypto_CipherMode_MaxValue = OEMCrypto_CipherMode_CBC, } OEMCryptoCipherMode; -/* - * OEMCrypto_EntitledContentKeyObject +/** * Contains encrypted content key data for loading into the sessions keytable. * The content key data is encrypted using AES-256-CBC encryption, with PKCS#7 * padding. - * entitlement_key_id - entitlement key id to be matched to key table. - * entitlement_key_id_length - length of entitlment_key_id in bytes (1 to 16). - * content_key_id - content key id to be loaded into key table. - * content_key_id_length - length of content key id in bytes (1 to 16). - * key_data_iv - the IV for performing AES-256-CBC decryption of the key data. - * key_data - encrypted content key data. - * key_data_length - length of key_data - 16 or 32 depending on intended use. + + * @param entitlement_key_id: entitlement key id to be matched to key table. + * @param entitlement_key_id_length: length of entitlment_key_id in bytes (1 to + * 16). + * @param content_key_id: content key id to be loaded into key table. + * @param content_key_id_length: length of content key id in bytes (1 to 16). + * @param key_data_iv: the IV for performing AES-256-CBC decryption of the key + * data. + * @param key_data: encrypted content key data. + * @param key_data_length: length of key_data: 16 or 32 depending on intended us + e. */ typedef struct { OEMCrypto_Substring entitlement_key_id; @@ -261,18 +372,31 @@ typedef struct { OEMCrypto_Substring content_key_data; } OEMCrypto_EntitledContentKeyObject; -/* - * OEMCrypto_KeyRefreshObject - * This structure is being deprecated. It is only used for legacy licenses. - * Points to the relevant fields for renewing a content key. The fields are - * extracted from the License Renewal Response message offered to - * OEMCrypto_RefreshKeys(). Each field points to one of the components of - * the key. - * key_id - the unique id of this key. - * key_control_iv - the IV for performing AES-128-CBC decryption of the - * key_control field. 16 bytes. - * key_control - the key control block. It is encrypted (AES-128-CBC) with - * the content key from the key_data field. 16 bytes. +/** + * This is a list of valid algorithms for OEMCrypto_Generic_* functions. + * Some are valid for encryption/decryption, and some for signing/verifying. + */ +typedef enum OEMCrypto_Algorithm { + OEMCrypto_AES_CBC_128_NO_PADDING = 0, + OEMCrypto_HMAC_SHA256 = 1, +} OEMCrypto_Algorithm; + +/// @} + +/// @addtogroup keyladder +/// @{ +/** + * This structure is being deprecated. It is only used for legacy licenses. + * Points to the relevant fields for renewing a content key. The fields are + * extracted from the License Renewal Response message offered to + * OEMCrypto_RefreshKeys(). Each field points to one of the components of + * the key. + + * @param key_id: the unique id of this key. + * @param key_control_iv: the IV for performing AES-128-CBC decryption of the + * key_control field. 16 bytes. + * @param key_control: the key control block. It is encrypted (AES-128-CBC) with + * the content key from the key_data field. 16 bytes. * * The key_data is unchanged from the original OEMCrypto_LoadKeys() call. Some * Key Control Block fields, especially those related to key lifetime, may @@ -287,17 +411,13 @@ typedef struct { OEMCrypto_Substring key_control; } OEMCrypto_KeyRefreshObject; -/* - * OEMCrypto_Algorithm - * This is a list of valid algorithms for OEMCrypto_Generic_* functions. - * Some are valid for encryption/decryption, and some for signing/verifying. - */ -typedef enum OEMCrypto_Algorithm { - OEMCrypto_AES_CBC_128_NO_PADDING = 0, - OEMCrypto_HMAC_SHA256 = 1, -} OEMCrypto_Algorithm; +/// @} -/* +/// @addtogroup usage_table +/// @{ + +#if 0 // If your compiler supports __attribute__((packed)). +/** * OEMCrypto_PST_Report is used to report an entry from the Usage Table. * * Platforms that have compilers that support packed structures, may use the @@ -306,7 +426,6 @@ typedef enum OEMCrypto_Algorithm { * * All fields are in network byte order. */ -#if 0 // If your compiler supports __attribute__((packed)). typedef struct { uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report. uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status) @@ -320,9 +439,8 @@ typedef struct { } __attribute__((packed)) OEMCrypto_PST_Report; #endif -/* - * OEMCrypto_Clock_Security_Level. - * Valid values for clock_security_level in OEMCrypto_PST_Report. +/** + * Valid values for clock_security_level in OEMCrypto_PST_Report. */ typedef enum OEMCrypto_Clock_Security_Level { kInsecureClock = 0, @@ -337,21 +455,27 @@ typedef uint8_t RSA_Padding_Scheme; // PKCS1 with block type 1 padding (only). #define kSign_PKCS1_Block1 ((RSA_Padding_Scheme)0x2) -/* +/// @} + +/// @addtogroup validation +/// @{ +/** * OEMCrypto_HDCP_Capability is used in the key control block to enforce HDCP * level, and in GetHDCPCapability for reporting. */ typedef enum OEMCrypto_HDCP_Capability { - HDCP_NONE = 0, // No HDCP supported, no secure data path. - HDCP_V1 = 1, // HDCP version 1.0 - HDCP_V2 = 2, // HDCP version 2.0 Type 1. - HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. - HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. - HDCP_V2_3 = 5, // HDCP version 2.3 Type 1. - HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. + HDCP_NONE = 0, // No HDCP supported, no secure data path. + HDCP_V1 = 1, // HDCP version 1.x + HDCP_V2 = 2, // HDCP version 2.0 Type 1. + HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. + HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. + HDCP_V2_3 = 5, // HDCP version 2.3 Type 1. + HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. } OEMCrypto_HDCP_Capability; -/* Return value for OEMCrypto_GetProvisioningMethod(). */ +/** + Return value for OEMCrypto_GetProvisioningMethod(). + */ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_ProvisioningError = 0, // Device cannot be provisioned. OEMCrypto_DrmCertificate = 1, // Device has baked in DRM certificate @@ -360,7 +484,7 @@ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. } OEMCrypto_ProvisioningMethod; -/* +/** * Flags indicating public/private key types supported. */ #define OEMCrypto_Supports_RSA_2048bit 0x1 @@ -370,14 +494,14 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_Supports_ECC_secp384r1 0x200 #define OEMCrypto_Supports_ECC_secp521r1 0x400 -/* +/** * Flags indicating full decrypt path hash supported. */ #define OEMCrypto_Hash_Not_Supported 0 #define OEMCrypto_CRC_Clear_Buffer 1 #define OEMCrypto_Partner_Defined_Hash 2 -/* +/** * Return values from OEMCrypto_GetAnalogOutputFlags. */ #define OEMCrypto_No_Analog_Output 0x0 @@ -387,7 +511,9 @@ typedef enum OEMCrypto_ProvisioningMethod { // Unknown_Analog_Output is used only for backwards compatibility. #define OEMCrypto_Unknown_Analog_Output (1<<31) -/* +/// @} + +/** * Obfuscation Renames. */ // clang-format off @@ -498,187 +624,164 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_MinorAPIVersion _oecc108 // clang-format on -/* - * OEMCrypto_SetSandbox +/// @addtogroup initcontrol +/// @{ + +/** + * This tells OEMCrypto which sandbox the current process belongs to. Any + * persistent memory used to store the generation number should be associated + * with this sandbox id. OEMCrypto can assume that this sandbox will be tied + * to the current process or VM until OEMCrypto_Terminate is called. See the + * section "VM and Sandbox Support" above for more details. * - * Description: - * This tells OEMCrypto which sandbox the current process belongs to. Any - * persistent memory used to store the generation number should be associated - * with this sandbox id. OEMCrypto can assume that this sandbox will be tied - * to the current process or VM until OEMCrypto_Terminate is called. See the - * section "VM and Sandbox Support" above for more details. + * If OEMCrypto does not support sandboxes, it will return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. On most platforms, this function will + * just return OEMCrypto_ERROR_NOT_IMPLEMENTED. If OEMCrypto supports + * sandboxes, this function returns OEMCrypto_SUCCESS on success, and + * OEMCrypto_ERROR_UNKNOWN_FAILURE on failure. * - * If OEMCrypto does not support sandboxes, it will return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. On most platforms, this function will - * just return OEMCrypto_ERROR_NOT_IMPLEMENTED. If OEMCrypto supports - * sandboxes, this function returns OEMCrypto_SUCCESS on success, and - * OEMCrypto_ERROR_UNKNOWN_FAILURE on failure. + * The CDM layer will call OEMCrypto_SetSandbox once before + * OEMCrypto_Initialize. After this function is called and returns success, + * it will be OEMCrypto's responsibility to keep calls to usage table + * functions separate, and to accept a call to OEMCrypto_Terminate for each + * sandbox. * - * The CDM layer will call OEMCrypto_SetSandbox once before - * OEMCrypto_Initialize. After this function is called and returns success, - * it will be OEMCrypto's responsibility to keep calls to usage table - * functions separate, and to accept a call to OEMCrypto_Terminate for each - * sandbox. + * @param[in] sandbox_id: a short string unique to the current sandbox. + * @param[in] sandbox_id_length: length of sandbox_id. * - * Parameters: - * [in] sandbox_id: a short string unique to the current sandbox. - * [in] sandbox_id_length: length of sandbox_id. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED sandbox functionality not supported * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware - * OEMCrypto_ERROR_NOT_IMPLEMENTED - sandbox functionality not supported - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. It is called once before * OEMCrypto_Initialize. * - * Version: + * @version * This method is new in version 15 of the API. */ OEMCryptoResult OEMCrypto_SetSandbox(const uint8_t* sandbox_id, size_t sandbox_id_length); -/* - * OEMCrypto_Initialize +/** + * Initialize the crypto firmware/hardware. * - * Description: - * Initialize the crypto firmware/hardware. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware * - * Parameters: - * None - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INIT_FAILED failed to initialize crypto hardware - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Initialize(void); -/* - * OEMCrypto_Terminate +/** + * Closes the crypto operation and releases all related resources. * - * Description: - * Closes the crypto operation and releases all related resources. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto + * hardware * - * Parameters: - * None - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_TERMINATE_FAILED failed to de-initialize crypto hardware - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. No other functions will be called before the * system is re-initialized. * - * Version: + * @version * This method is supported by all API versions. */ OEMCryptoResult OEMCrypto_Terminate(void); -/* - * OEMCrypto_OpenSession +/// @} + +/// @addtogroup keyladder +/// @{ +/** + * Open a new crypto security engine context. The security engine hardware + * and firmware shall acquire resources that are needed to support the + * session, and return a session handle that identifies that session in + * future calls. * - * Description: - * Open a new crypto security engine context. The security engine hardware - * and firmware shall acquire resources that are needed to support the - * session, and return a session handle that identifies that session in - * future calls. + * This function shall call ODK_InitializeSessionValues to initialize the + * session's clock values, timer values, and nonce values. + * ODK_InitializeSessionValues is described in the document "License Duration + * and Renewal", to initialize the session's clock values. * - * This function shall call ODK_InitializeSessionValues to initialize the - * session's clock values, timer values, and nonce values. - * ODK_InitializeSessionValues is described in the document "License Duration - * and Renewal", to initialize the session's clock values. + * @param[out] session: an opaque handle that the crypto firmware uses to + * identify the session. * - * Parameters: - * [out] session: an opaque handle that the crypto firmware uses to identify - * the session. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions are open - * OEMCrypto_ERROR_OPEN_SESSION_FAILED there is a resource issue or the + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_TOO_MANY_SESSIONS failed because too many sessions + * are open + * @retval OEMCrypto_ERROR_OPEN_SESSION_FAILED there is a resource issue or the * security engine is not properly initialized. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Initialization Function" and will not be called * simultaneously with any other function, as if the CDM holds a write lock * on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session); -/* - * OEMCrypto_CloseSession +/** + * Closes the crypto security engine session and frees any associated + * resources. If this session is associated with a Usage Entry, all resident + * memory associated with it will be freed. It is the CDM layer's + * responsibility to call OEMCrypto_UpdateUsageEntry before closing the + * session. * - * Description: - * Closes the crypto security engine session and frees any associated - * resources. If this session is associated with a Usage Entry, all resident - * memory associated with it will be freed. It is the CDM layer's - * responsibility to call OEMCrypto_UpdateUsageEntry before closing the - * session. + * @param[in] session: handle for the session to be closed. * - * Parameters: - * [in] session: handle for the session to be closed. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * @retval OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or + * the security engine is not properly initialized. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. - * OEMCrypto_ERROR_CLOSE_SESSION_FAILED illegal/unrecognized handle or the - * security engine is not properly initialized. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Initialization Function" and will not be called * simultaneously with any other function, as if the CDM holds a write lock * on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); -/* - * OEMCrypto_GenerateDerivedKeys +/** + * Generates three secondary keys, mac_key[server], mac_key[client], and + * encrypt_key, for handling signing and content key decryption under the + * license server protocol for CENC. * - * Description: - * Generates three secondary keys, mac_key[server], mac_key[client], and - * encrypt_key, for handling signing and content key decryption under the - * license server protocol for CENC. + * Refer to the Key Derivation section above for more details. This function + * computes the AES-128-CMAC of the enc_key_context and stores it in secure + * memory as the encrypt_key. It then computes four cycles of AES-128-CMAC of + * the mac_key_context and stores it in the mac_keys -- the first two cycles + * generate the mac_key[server] and the second two cycles generate the + * mac_key[client]. These two keys will be stored until the next call to + * OEMCrypto_LoadKeys(). The device key from the keybox is used as the key + * for the AES-128-CMAC. * - * Refer to the Key Derivation section above for more details. This function - * computes the AES-128-CMAC of the enc_key_context and stores it in secure - * memory as the encrypt_key. It then computes four cycles of AES-128-CMAC of - * the mac_key_context and stores it in the mac_keys -- the first two cycles - * generate the mac_key[server] and the second two cycles generate the - * mac_key[client]. These two keys will be stored until the next call to - * OEMCrypto_LoadKeys(). The device key from the keybox is used as the key - * for the AES-128-CMAC. - * - * Parameters: - * [in] session: handle for the session to be used. - * [in] mac_key_context: pointer to memory containing context data for - * computing the HMAC generation key. - * [in] mac_key_context_length: length of the HMAC key context data, in bytes. - * [in] enc_key_context: pointer to memory containing context data for - * computing the encryption key. - * [in] enc_key_context_length: length of the encryption key context data, in - * bytes. + * @param[in] session: handle for the session to be used. + * @param[in] mac_key_context: pointer to memory containing context data for + * computing the HMAC generation key. + * @param[in] mac_key_context_length: length of the HMAC key context data, in + * bytes. + * @param[in] enc_key_context: pointer to memory containing context data for + * computing the encryption key. + * @param[in] enc_key_context_length: length of the encryption key context data, + * in bytes. * * Results: * mac_key[server]: the 256 bit mac key is generated and stored in secure @@ -688,31 +791,30 @@ OEMCryptoResult OEMCrypto_CloseSession(OEMCrypto_SESSION session); * enc_key: the 128 bit encryption key is generated and stored in secure * memory. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support mac_key_context and enc_key_context sizes as * described in the section OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffers are * too large. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( @@ -721,50 +823,47 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( const OEMCrypto_SharedMemory* enc_key_context, size_t enc_key_context_length); -/* - * OEMCrypto_DeriveKeysFromSessionKey +/** + * Generates three secondary keys, mac_key[server], mac_key[client] and + * encrypt_key, for handling signing and content key decryption under the + * license server protocol for CENC. * - * Description: - * Generates three secondary keys, mac_key[server], mac_key[client] and - * encrypt_key, for handling signing and content key decryption under the - * license server protocol for CENC. + * This function is similar to OEMCrypto_GenerateDerivedKeys, except that it + * uses a session key to generate the secondary keys instead of the Widevine + * Keybox device key. These three keys will be stored in secure memory until + * the next call to LoadLicense or LoadProvisioning. * - * This function is similar to OEMCrypto_GenerateDerivedKeys, except that it - * uses a session key to generate the secondary keys instead of the Widevine - * Keybox device key. These three keys will be stored in secure memory until - * the next call to LoadLicense or LoadProvisioning. + * If the session's private key is an RSA key, then the session key is passed + * in encrypted by the device RSA public key as the derivation_key, and must + * be decrypted with the RSA private key before use. * - * If the session's private key is an RSA key, then the session key is passed - * in encrypted by the device RSA public key as the derivation_key, and must - * be decrypted with the RSA private key before use. + * If the sesion's private key is an ECC key, then the session key is the + * SHA256 of the shared secret key calculated by ECDH between the device's + * ECC private key and the derivation_key. See the document "OEMCrypto + * Elliptic Curve Support" for details. * - * If the sesion's private key is an ECC key, then the session key is the - * SHA256 of the shared secret key calculated by ECDH between the device's - * ECC private key and the derivation_key. See the document "OEMCrypto - * Elliptic Curve Support" for details. + * Once the enc_key and mac_keys have been generated, all calls to LoadKeys + * or LoadLicense proceed in the same manner for license requests using RSA + * or using a Widevine keybox token. * - * Once the enc_key and mac_keys have been generated, all calls to LoadKeys - * or LoadLicense proceed in the same manner for license requests using RSA - * or using a Widevine keybox token. - * - * Verification: + * @verification * If the RSA key's allowed_schemes is not kSign_RSASSA_PSS, then no keys are * derived and the error OEMCrypto_ERROR_INVALID_RSA_KEY is returned. An RSA * key cannot be used for both deriving session keys and also for PKCS1 * signatures. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] derivation_key: session key, encrypted with the public RSA key (from - * the DRM certifcate) using RSA-OAEP. - * [in] derivation_key_length: length of derivation_key, in bytes. - * [in] mac_key_context: pointer to memory containing context data for - * computing the HMAC generation key. - * [in] mac_key_context_length: length of the HMAC key context data, in bytes. - * [in] enc_key_context: pointer to memory containing context data for - * computing the encryption key. - * [in] enc_key_context_length: length of the encryption key context data, in - * bytes. + * @param[in] session: handle for the session to be used. + * @param[in] derivation_key: session key, encrypted with the public RSA key + * (from the DRM certifcate) using RSA-OAEP. + * @param[in] derivation_key_length: length of derivation_key, in bytes. + * @param[in] mac_key_context: pointer to memory containing context data for + * computing the HMAC generation key. + * @param[in] mac_key_context_length: length of the HMAC key context data, in + * bytes. + * @param[in] enc_key_context: pointer to memory containing context data for + * computing the encryption key. + * @param[in] enc_key_context_length: length of the encryption key context data, + * in bytes. * * Results: * mac_key[server]: the 256 bit mac key is generated and stored in secure @@ -774,31 +873,30 @@ OEMCryptoResult OEMCrypto_GenerateDerivedKeys( * enc_key: the 128 bit encryption key is generated and stored in secure * memory. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_DEVICE_NOT_RSA_PROVISIONED + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support mac_key_context and enc_key_context sizes as * described in the section OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffers are * too large. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( @@ -808,575 +906,544 @@ OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( const OEMCrypto_SharedMemory* enc_key_context, size_t enc_key_context_length); -/* - * OEMCrypto_GenerateNonce +/** + * Generates a 32-bit nonce to detect possible replay attack on the key + * control block. The nonce is stored in secure memory and will be used in + * the license or provisioning request. * - * Description: - * Generates a 32-bit nonce to detect possible replay attack on the key - * control block. The nonce is stored in secure memory and will be used in - * the license or provisioning request. + * Because the nonce will be used to prevent replay attacks, it is desirable + * that a rogue application cannot rapidly call this function until a + * repeated nonce is created randomly. This is called a nonce flood. With + * this in mind, if more than 200 nonces are requested within one second, + * OEMCrypto will return an error after the 200th and not generate any more + * nonces for the rest of the second. After an error, if the application + * waits at least one second before requesting more nonces, then OEMCrypto + * will reset the error condition and generate valid nonces again. * - * Because the nonce will be used to prevent replay attacks, it is desirable - * that a rogue application cannot rapidly call this function until a - * repeated nonce is created randomly. This is called a nonce flood. With - * this in mind, if more than 200 nonces are requested within one second, - * OEMCrypto will return an error after the 200th and not generate any more - * nonces for the rest of the second. After an error, if the application - * waits at least one second before requesting more nonces, then OEMCrypto - * will reset the error condition and generate valid nonces again. + * The nonce should be stored in the session's ODK_NonceValue field by + * calling the function ODK_SetNonceValue(&nonce_values, nonce). The ODK + * functions are documented in "Widevine Core Message Serialization". * - * The nonce should be stored in the session's ODK_NonceValue field by - * calling the function ODK_SetNonceValue(&nonce_values, nonce). The ODK - * functions are documented in "Widevine Core Message Serialization". + * This function shall only be called at most once per open session. It shall + * only be called before signing either a provisioning request or a license + * request. If an attempt is made to generate a nonce while in the wrong + * state, an error of OEMCrypto_ERROR_INVALID_CONTEXT is returned. * - * This function shall only be called at most once per open session. It shall - * only be called before signing either a provisioning request or a license - * request. If an attempt is made to generate a nonce while in the wrong - * state, an error of OEMCrypto_ERROR_INVALID_CONTEXT is returned. - * - * Parameters: - * [in] session: handle for the session to be used. - * [out] nonce: pointer to memory to receive the computed nonce. + * @param[in] session: handle for the session to be used. + * @param[out] nonce: pointer to memory to receive the computed nonce. * * Results: - * nonce: the nonce is also stored in secure memory. Each session should - * store 4 nonces. + * nonce: the nonce is also stored in secure memory. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Initialization Function" and will not be called * simultaneously with any other function, as if the CDM holds a write lock * on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, uint32_t* nonce); -/* - * OEMCrypto_PrepAndSignLicenseRequest +/** + * OEMCrypto will use ODK_PrepareCoreLicenseRequest to prepare the core + * message. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall sign the + * the message body using the DRM certificate's private key. If it returns an + * error, the error should be returned by OEMCrypto to the CDM layer. + * ODK_PrepareCoreLicenseRequest is described in the document "Widevine Core + * Message Serialization". * - * Description: - * OEMCrypto will use ODK_PrepareCoreLicenseRequest to prepare the core - * message. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall sign the - * the message body using the DRM certificate's private key. If it returns an - * error, the error should be returned by OEMCrypto to the CDM layer. - * ODK_PrepareCoreLicenseRequest is described in the document "Widevine Core - * Message Serialization". + * The message body is the buffer starting at message + core_message_size, + * and with length message_length - core_message_size. The reason OEMCrypto + * only signs the message body and not the entire message is to allow a v16 + * device to request a license from a v15 license server. * - * The message body is the buffer starting at message + core_message_size, - * and with length message_length - core_message_size. The reason OEMCrypto - * only signs the message body and not the entire message is to allow a v16 - * device to request a license from a v15 license server. + * If the session's private RSA key has an "allowed_schemes" bit field, then + * it must be 0x1 (RSASSA-PSS with SHA1). If not, then an error of + * OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. * - * If the session's private RSA key has an "allowed_schemes" bit field, then - * it must be 0x1 (RSASSA-PSS with SHA1). If not, then an error of - * OEMCrypto_ERROR_SIGNATURE_FAILURE shall be returned. + * OEMCrypto shall compute a hash of the core license request. The core + * license request is the buffer starting at message and with length + * core_message_size. The hash will be saved with the session and verified + * that it matches a hash in the license response. * - * OEMCrypto shall compute a hash of the core license request. The core - * license request is the buffer starting at message and with length - * core_message_size. The hash will be saved with the session and verified - * that it matches a hash in the license response. + * OEMCrypto shall also call the function ODK_InitializeClockValues, + * described in the document "License Duration and Renewal", to initialize + * the session's clock values. * - * OEMCrypto shall also call the function ODK_InitializeClockValues, - * described in the document "License Duration and Renewal", to initialize - * the session's clock values. + * Refer to the Signing Messages Sent to a Server section above for more + * details about the signature algorithm. * - * Refer to the Signing Messages Sent to a Server section above for more - * details about the signature algorithm. + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. * - * NOTE: if signature pointer is null and/or input signature_length is zero, - * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * signature_length to the size needed to receive the output signature. + * @param[in] session: handle for the session to be used. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via the ODK library. + * @param[in] message_length: length of the entire message buffer. + * @param[in,out] core_message_size: length of the core message at the beginning + * of the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * @param[out] signature: pointer to memory to receive the computed signature. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in/out] message: Pointer to memory for the entire message. Modified by - * OEMCrypto via the ODK library. - * [in] message_length: length of the entire message buffer. - * [in/out] core_message_size: length of the core message at the beginning of - * the message. (in) size of buffer reserved for the core message, in - * bytes. (out) actual length of the core message, in bytes. - * [out] signature: pointer to memory to receive the computed signature. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the signature. + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the signature. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_PrepAndSignLicenseRequest( OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_size, uint8_t* signature, size_t* signature_length); -/* - * OEMCrypto_PrepAndSignRenewalRequest +/** + * OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the + * document "Widevine Core Message Serialization", to prepare the core + * message. * - * Description: - * OEMCrypto will use ODK_PrepareCoreRenewalRequest, as described in the - * document "Widevine Core Message Serialization", to prepare the core - * message. + * If it returns an error, the error should be returned by OEMCrypto to the + * CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto computes the + * signature using the renewal mac key which was delivered in the license via + * LoadLicense. * - * If it returns an error, the error should be returned by OEMCrypto to the - * CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto computes the - * signature using the renewal mac key which was delivered in the license via - * LoadLicense. + * If nonce_values.api_level is 16, then OEMCrypto shall compute the + * signature of the entire message using the session's client renewal mac + * key. The entire message is the buffer starting at message with length + * message_length. * - * If nonce_values.api_level is 16, then OEMCrypto shall compute the - * signature of the entire message using the session's client renewal mac - * key. The entire message is the buffer starting at message with length - * message_length. + * If nonce_values.api_major_version is 15, then OEMCrypto shall compute the + * signature of the message body using the session's client renewal mac key. + * The message body is the buffer starting at message+core_message_size with + * length message_length - core_message_size. If the session has not had a + * license loaded, it will use the usage entries client mac key to sign the + * message body. * - * If nonce_values.api_major_version is 15, then OEMCrypto shall compute the - * signature of the message body using the session's client renewal mac key. - * The message body is the buffer starting at message+core_message_size with - * length message_length - core_message_size. If the session has not had a - * license loaded, it will use the usage entries client mac key to sign the - * message body. + * This function generates a HMAC-SHA256 signature using the mac_key[client] + * for license request signing under the license server protocol for CENC. * - * This function generates a HMAC-SHA256 signature using the mac_key[client] - * for license request signing under the license server protocol for CENC. + * The key used for signing should be the mac_key[client] that was generated + * for this session or loaded for this session by OEMCrypto_LoadKeys, + * OEMCrypto_LoadLicense, or OEMCrypto_LoadUsageEntry. * - * The key used for signing should be the mac_key[client] that was generated - * for this session or loaded for this session by OEMCrypto_LoadKeys, - * OEMCrypto_LoadLicense, or OEMCrypto_LoadUsageEntry. + * Refer to the Signing Messages Sent to a Server section above for more + * details. * - * Refer to the Signing Messages Sent to a Server section above for more - * details. + * If a usage entry has been loaded, but keys have not been loaded through + * OEMCrypto_LoadKeys, then the derived mac keys and the keys in the usage + * entry may be different. In this case, the mac keys specified in the usage + * entry should be used. * - * If a usage entry has been loaded, but keys have not been loaded through - * OEMCrypto_LoadKeys, then the derived mac keys and the keys in the usage - * entry may be different. In this case, the mac keys specified in the usage - * entry should be used. + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. * - * NOTE: if signature pointer is null and/or input signature_length is zero, - * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * signature_length to the size needed to receive the output signature. + * @param[in] session: handle for the session to be used. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via the ODK library. + * @param[in] message_length: length of the entire message buffer. + * @param[in,out] core_message_size: length of the core message at the beginning + * of the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * @param[out] signature: pointer to memory to receive the computed signature. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in/out] message: Pointer to memory for the entire message. Modified by - * OEMCrypto via the ODK library. - * [in] message_length: length of the entire message buffer. - * [in/out] core_message_size: length of the core message at the beginning of - * the message. (in) size of buffer reserved for the core message, in - * bytes. (out) actual length of the core message, in bytes. - * [out] signature: pointer to memory to receive the computed signature. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the signature. + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the signature. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_size, uint8_t* signature, size_t* signature_length); -/* - * OEMCrypto_PrepAndSignProvisioningRequest +/** + * OEMCrypto will use OEMCrypto_PrepAndSignProvisioningRequest, as described + * in the document "Widevine Core Message Serialization", to prepare the core + * message. If it returns an error, the error should be returned by OEMCrypto + * to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall + * compute the signature of the entire message. The entire message is the + * buffer starting at message with length message_length. * - * Description: - * OEMCrypto will use OEMCrypto_PrepAndSignProvisioningRequest, as described - * in the document "Widevine Core Message Serialization", to prepare the core - * message. If it returns an error, the error should be returned by OEMCrypto - * to the CDM layer. If it returns OEMCrypto_SUCCESS, then OEMCrypto shall - * compute the signature of the entire message. The entire message is the - * buffer starting at message with length message_length. + * For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign + * the request with the session's derived client mac key from the previous + * call to OEMCrypto_GenerateDerivedKeys. * - * For a device that has a keybox, i.e. Provisioning 2.0, OEMCrypto will sign - * the request with the session's derived client mac key from the previous - * call to OEMCrypto_GenerateDerivedKeys. + * For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto + * will sign the request with the private key associated with the OEM + * Certificate. The key shall have been loaded by a previous call to + * OEMCrypto_LoadDRMPrivateKey. * - * For a device that has an OEM Certificate, i.e. Provisioning 3.0, OEMCrypto - * will sign the request with the private key associated with the OEM - * Certificate. The key shall have been loaded by a previous call to - * OEMCrypto_LoadDRMPrivateKey. + * Refer to the Signing Messages Sent to a Server section above for more + * details. * - * Refer to the Signing Messages Sent to a Server section above for more - * details. + * NOTE: if signature pointer is null and/or input signature_length is zero, + * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * signature_length to the size needed to receive the output signature. * - * NOTE: if signature pointer is null and/or input signature_length is zero, - * this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * signature_length to the size needed to receive the output signature. + * @param[in] session: handle for the session to be used. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * OEMCrypto via the ODK library. + * @param[in] message_length: length of the entire message buffer. + * @param[in,out] core_message_size: length of the core message at the beginning + * of the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * @param[out] signature: pointer to memory to receive the computed signature. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in/out] message: Pointer to memory for the entire message. Modified by - * OEMCrypto via the ODK library. - * [in] message_length: length of the entire message buffer. - * [in/out] core_message_size: length of the core message at the beginning of - * the message. (in) size of buffer reserved for the core message, in - * bytes. (out) actual length of the core message, in bytes. - * [out] signature: pointer to memory to receive the computed signature. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the signature. + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the signature. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_PrepAndSignProvisioningRequest( OEMCrypto_SESSION session, uint8_t* message, size_t message_length, size_t* core_message_size, uint8_t* signature, size_t* signature_length); -/* - * OEMCrypto_LoadSRM +/** + * Verify and install a new SRM file. The device shall install the new file + * only if verification passes. If verification fails, the existing SRM will + * be left in place. Verification is defined by DCP, and includes + * verification of the SRM's signature and verification that the SRM version + * number will not be decreased. See the section HDCP SRM Update above for + * more details about the SRM. This function is for devices that support HDCP + * v2.2 or higher and wish to receive 4k content. * - * Description: - * Verify and install a new SRM file. The device shall install the new file - * only if verification passes. If verification fails, the existing SRM will - * be left in place. Verification is defined by DCP, and includes - * verification of the SRM's signature and verification that the SRM version - * number will not be decreased. See the section HDCP SRM Update above for - * more details about the SRM. This function is for devices that support HDCP - * v2.2 or higher and wish to receive 4k content. + * @param[in] bufer: buffer containing the SRM + * @param[in] buffer_length: length of the SRM, in bytes. * - * Parameters: - * [in] bufer: buffer containing the SRM - * [in] buffer_length: length of the SRM, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS - if the file was valid and was installed. - * OEMCrypto_ERROR_INVALID_CONTEXT - if the SRM version is too low, or the - * file is corrupted. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - If the signature is invalid. - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - if the buffer is too large for the + * @retval OEMCrypto_SUCCESS if the file was valid and was installed. + * @retval OEMCrypto_ERROR_INVALID_CONTEXT if the SRM version is too low, or + * the file is corrupted. + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE If the signature is invalid. + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is too large for the * device. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * The size of the buffer is determined by the HDCP specification. * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_LoadKeys +/** + * Install a set of keys for performing decryption in the current session. + * This function will be deprecated and will only be used for legacy license + * from a license server that does not yet support the v16 interface. * - * Description: - * Install a set of keys for performing decryption in the current session. - * This function will be deprecated and will only be used for legacy license - * from a license server that does not yet support the v16 interface. + * The relevant fields have been extracted from the License Response protocol + * message, but the entire message and associated signature are provided so + * the message can be verified (using HMAC-SHA256 with the derived + * mac_key[server]). If the signature verification fails, ignore all other + * arguments and return OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the + * keys to the session context. * - * The relevant fields have been extracted from the License Response protocol - * message, but the entire message and associated signature are provided so - * the message can be verified (using HMAC-SHA256 with the derived - * mac_key[server]). If the signature verification fails, ignore all other - * arguments and return OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the - * keys to the session context. + * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and + * the IV given in the KeyObject. Each key control block will be decrypted + * using the first 128 bits of the corresponding content key (AES-128-CBC) + * and the IV given in the KeyObject. * - * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and - * the IV given in the KeyObject. Each key control block will be decrypted - * using the first 128 bits of the corresponding content key (AES-128-CBC) - * and the IV given in the KeyObject. + * If its length is not zero, enc_mac_keys will be used to create new + * mac_keys. After all keys have been decrypted and validated, the new + * mac_keys are decrypted with the current encrypt_key and the offered IV. + * The new mac_keys replaces the current mac_keys for future calls to + * OEMCrypto_RefreshKeys(). The first 256 bits of the mac_keys become the + * mac_key[server] and the following 256 bits of the mac_keys become the + * mac_key[client]. * - * If its length is not zero, enc_mac_keys will be used to create new - * mac_keys. After all keys have been decrypted and validated, the new - * mac_keys are decrypted with the current encrypt_key and the offered IV. - * The new mac_keys replaces the current mac_keys for future calls to - * OEMCrypto_RefreshKeys(). The first 256 bits of the mac_keys become the - * mac_key[server] and the following 256 bits of the mac_keys become the - * mac_key[client]. + * The mac_key and encrypt_key were generated and stored by the previous call + * to OEMCrypto_GenerateDerivedKeys() or + * OEMCrypto_DeriveKeysFromSessionKey(). The nonce was generated and stored + * in the session's nonce_values by the previous call to + * OEMCrypto_GenerateNonce(). * - * The mac_key and encrypt_key were generated and stored by the previous call - * to OEMCrypto_GenerateDerivedKeys() or - * OEMCrypto_DeriveKeysFromSessionKey(). The nonce was generated and stored - * in the session's nonce_values by the previous call to - * OEMCrypto_GenerateNonce(). + * This session's elapsed time clock is started at 0. The clock will be used + * in OEMCrypto_DecryptCENC(). * - * This session's elapsed time clock is started at 0. The clock will be used - * in OEMCrypto_DecryptCENC(). + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). * - * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). + * Refer to the Verification of Messages from a Server section above for more + * details. * - * Refer to the Verification of Messages from a Server section above for more - * details. + * If the parameter license_type is OEMCrypto_ContentLicense, then the fields + * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * content_key_id and content_key_data fields of the key table entry. In this + * case, entitlement key ids and entitlement key data is left blank. * - * If the parameter license_type is OEMCrypto_ContentLicense, then the fields - * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * content_key_id and content_key_data fields of the key table entry. In this - * case, entitlement key ids and entitlement key data is left blank. + * If the parameter license_type is OEMCrypto_EntitlementLicense, then the + * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * entitlement_key_id and entitlement_key_data fields of the key table entry. + * In this case, content key ids and content key data will be loaded later + * with a call to OEMCrypto_LoadEntitledContentKeys(). * - * If the parameter license_type is OEMCrypto_EntitlementLicense, then the - * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * entitlement_key_id and entitlement_key_data fields of the key table entry. - * In this case, content key ids and content key data will be loaded later - * with a call to OEMCrypto_LoadEntitledContentKeys(). + * OEMCrypto may assume that the key_id_length is at most 16. However, + * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. * - * OEMCrypto may assume that the key_id_length is at most 16. However, - * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. + * OEMCrypto shall handle at least 20 keys per session. This allows a single + * license to contain separate keys for 3 key rotations (previous interval, + * current interval, next interval) times 4 content keys (audio, SD, HD, UHD) + * plus up to 8 keys for watermarks. * - * OEMCrypto shall handle at least 20 keys per session. This allows a single - * license to contain separate keys for 3 key rotations (previous interval, - * current interval, next interval) times 4 content keys (audio, SD, HD, UHD) - * plus up to 8 keys for watermarks. + * After a call to OEMCrypto_LoadKeys, oemcrypto should clear the encrypt_key + * for the session. * - * After a call to OEMCrypto_LoadKeys, oemcrypto should clear the encrypt_key - * for the session. + * @verification + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * 1. The signature of the message shall be computed, and the API shall + * verify the computed signature matches the signature passed in. If + * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature + * verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * 2. If there already is a license loaded into this session, return + * OEMCrypto_ERROR_LICENSE_RELOAD. + * 3. The enc_mac_keys substring must either have zero length, or satisfy + * the range check. I.e. (offset < message_length) && (offset + length + * <= message_length) && (offset <= offset + length), and offset + length + * does not cause an integer overflow. If it does not have zero length, + * then enc_mac_keys_iv must not have zero length, and must also satisfy + * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If + * the length is zero, then OEMCrypto may assume that the offset is also + * zero. + * 4. The API shall verify that each substring in each KeyObject points to + * a location in the message. I.e. (offset < message_length) && + * (offset + length <= message_length) && (offset <= offset + length), + * and offset + length does not cause an integer overflow, for each of + * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, + * return OEMCrypto_ERROR_INVALID_CONTEXT. + * 5. Each key's control block, after decryption, shall have a valid + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 6. If any key control block has the Nonce_Enabled bit set, that key's + * Nonce field shall match a nonce in the cache. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that + * nonce from the cache. Note that all the key control blocks in a + * particular call shall have the same nonce value. + * 7. If any key control block has the Require_AntiRollback_Hardware bit + * set, and the device does not protect the usage table from rollback, + * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 8. If the key control block has a nonzero Replay_Control, then the + * verification described below is also performed. + * 9. If the key control block has the bit SRMVersionRequired is set, then + * the verification described below is also performed. If the SRM + * requirement is not met, then the key control block's HDCP_Version + * will be changed to 0xF - local display only. + * 10. If key_array_length == 0, then return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 11. If this session is associated with a usage table entry, and that + * entry is marked as "inactive" (either kInactiveUsed or + * kInactiveUnused), then the keys are not loaded, and the error + * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. + * 12. The data in enc_mac_keys_iv is not identical to the 16 bytes before + * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. + * Usage Table and Provider Session Token (pst) + * If a key control block has a nonzero value for Replay_Control, then all + * keys in this license will have the same value for Replay_Control. In this + * case, the following additional checks are performed. + * - The substring pst must have nonzero length and must satisfy the range + * check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * - The session must be associated with a usage table entry, either + * created via OEMCrypto_CreateNewUsageEntry or loaded via + * OEMCrypto_LoadUsageEntry. + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the + * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If + * an existing entry was reloaded, an error + * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. + * OEMCrypto will then copy the pst and the mac keys to the usage entry, + * and set the status to Unused. This Replay_Control prevents the + * license from being loaded more than once, and will be used for online + * streaming. + * - If Replay_Control is 2 = "Require existing Session Usage table entry + * or Nonce", then OEMCrypto will behave slightly differently on the + * first call to LoadKeys for this license. + * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry + * for this session, then OEMCrypto will verify the nonce for each + * key. OEMCrypto will copy the pst and mac keys to the usage + * entry. The license received time of the entry will be updated + * to the current time, and the status will be set to Unused. + * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for + * this session, then OEMCrypto will NOT verify the nonce for each + * key. Instead, it will verify that the pst passed in matches + * that in the entry. Also, the entry's mac keys will be verified + * against the current session's mac keys. This allows an offline + * license to be reloaded but maintain continuity of the playback + * times from one session to the next. + * * If the nonce is not valid and a usage entry was not loaded, the + * return error is OEMCrypto_ERROR_INVALID_NONCE. + * * If the loaded usage entry has a pst that does not match, + * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. + * * If the loaded usage entry has mac keys that do not match the + * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used with the Usage Entry -- i.e. the new keys are stored in the + * usage table when creating a new entry, or the new keys are verified + * against those in the usage table if there is an existing entry. If + * LoadKeys does not update the mac keys, the existing session mac keys are + * used. * - * Verification: - * The following checks should be performed. If any check fails, an error is - * returned, and none of the keys are loaded. - * 1. The signature of the message shall be computed, and the API shall - * verify the computed signature matches the signature passed in. If - * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature - * verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). - * 2. If there already is a license loaded into this session, return - * OEMCrypto_ERROR_LICENSE_RELOAD. - * 3. The enc_mac_keys substring must either have zero length, or satisfy - * the range check. I.e. (offset < message_length) && (offset + length - * < message_length) && (offset < offset + length),and offset + length - * does not cause an integer overflow. If it does not have zero length, - * then enc_mac_keys_iv must not have zero length, and must also satisfy - * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If - * the length is zero, then OEMCrypto may assume that the offset is also - * zero. - * 4. The API shall verify that each substring in each KeyObject points to - * a location in the message. I.e. (offset < message_length) && - * (offset + length < message_length) && (offset < offset + length) and - * offset + length does not cause an integer overflow, for each of - * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, - * return OEMCrypto_ERROR_INVALID_CONTEXT. - * 5. Each key's control block, after decryption, shall have a valid - * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 6. If any key control block has the Nonce_Enabled bit set, that key's - * Nonce field shall match a nonce in the cache. If not, return - * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that - * nonce from the cache. Note that all the key control blocks in a - * particular call shall have the same nonce value. - * 7. If any key control block has the Require_AntiRollback_Hardware bit - * set, and the device does not protect the usage table from rollback, - * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 8. If the key control block has a nonzero Replay_Control, then the - * verification described below is also performed. - * 9. If the key control block has the bit SRMVersionRequired is set, then - * the verification described below is also performed. If the SRM - * requirement is not met, then the key control block's HDCP_Version - * will be changed to 0xF - local display only. - * 10. If key_array_length == 0, then return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 11. If this session is associated with a usage table entry, and that - * entry is marked as "inactive" (either kInactiveUsed or - * kInactiveUnused), then the keys are not loaded, and the error - * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. - * 12. The data in enc_mac_keys_iv is not identical to the 16 bytes before - * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. - * Usage Table and Provider Session Token (pst) - * If a key control block has a nonzero value for Replay_Control, then all - * keys in this license will have the same value for Replay_Control. In this - * case, the following additional checks are performed. - * - The substring pst must have nonzero length and must satisfy the range - * check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * - The session must be associated with a usage table entry, either - * created via OEMCrypto_CreateNewUsageEntry or loaded via - * OEMCrypto_LoadUsageEntry. - * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a - * nonce check as described above. OEMCrypto will verify that the - * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If - * an existing entry was reloaded, an error - * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. - * OEMCrypto will then copy the pst and the mac keys to the usage entry, - * and set the status to Unused. This Replay_Control prevents the - * license from being loaded more than once, and will be used for online - * streaming. - * - If Replay_Control is 2 = "Require existing Session Usage table entry - * or Nonce", then OEMCrypto will behave slightly differently on the - * first call to LoadKeys for this license. - * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry - * for this session, then OEMCrypto will verify the nonce for each - * key. OEMCrypto will copy the pst and mac keys to the usage - * entry. The license received time of the entry will be updated - * to the current time, and the status will be set to Unused. - * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for - * this session, then OEMCrypto will NOT verify the nonce for each - * key. Instead, it will verify that the pst passed in matches - * that in the entry. Also, the entry's mac keys will be verified - * against the current session's mac keys. This allows an offline - * license to be reloaded but maintain continuity of the playback - * times from one session to the next. - * * If the nonce is not valid and a usage entry was not loaded, the - * return error is OEMCrypto_ERROR_INVALID_NONCE. - * * If the loaded usage entry has a pst that does not match, - * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. - * * If the loaded usage entry has mac keys that do not match the - * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. - * Note: If LoadKeys updates the mac keys, then the new updated mac keys will - * be used with the Usage Entry -- i.e. the new keys are stored in the - * usage table when creating a new entry, or the new keys are verified - * against those in the usage table if there is an existing entry. If - * LoadKeys does not update the mac keys, the existing session mac keys are - * used. + * Sessions that are associated with an entry will need to be able to update + * and verify the status of the entry, and the time stamps in the entry. * - * Sessions that are associated with an entry will need to be able to update - * and verify the status of the entry, and the time stamps in the entry. + * Devices that do not support the Usage Table will return + * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. * - * Devices that do not support the Usage Table will return - * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. + * Timer Update + * After verification, the session's clock and timer values are updated by + * calling the function ODK_InitializeV15Values as described in the document + * "Widevine Core Message Serialization". * - * Timer Update - * After verification, the session's clock and timer values are updated by - * calling the function ODK_InitializeV15Values as described in the document - * "Widevine Core Message Serialization". + * SRM Restriction Data * - * SRM Restriction Data + * If any key control block has the flag SRMVersionRequired set, then the + * following verification is also performed. * - * If any key control block has the flag SRMVersionRequired set, then the - * following verification is also performed. + * 1. The substring srm_restriction_data must have nonzero length and must + * satisfy the range check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 2. The first 8 bytes of srm_restriction_data must match the string + * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 3. The next 4 bytes of srm_restriction_data will be converted from + * network byte order. If the current SRM installed on the device has a + * version number less than this, then the SRM requirement is not met. + * If the device does not support SRM files, or OEMCrypto cannot + * determine the current SRM version number, then the SRM requirement is + * not met. + * Note: if the current SRM version requirement is not met, LoadKeys will + * still succeed and the keys will be loaded. However, those keys with the + * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - + * local display only. Any future call to SelectKey for these keys while + * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP + * at that time. * - * 1. The substring srm_restriction_data must have nonzero length and must - * satisfy the range check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 2. The first 8 bytes of srm_restriction_data must match the string - * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 3. The next 4 bytes of srm_restriction_data will be converted from - * network byte order. If the current SRM installed on the device has a - * version number less than this, then the SRM requirement is not met. - * If the device does not support SRM files, or OEMCrypto cannot - * determine the current SRM version number, then the SRM requirement is - * not met. - * Note: if the current SRM version requirement is not met, LoadKeys will - * still succeed and the keys will be loaded. However, those keys with the - * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - - * local display only. Any future call to SelectKey for these keys while - * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP - * at that time. + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. + * @param[in] enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. + * @param[in] enc_mac_keys: encrypted mac_keys for generating new mac_keys. + * Size is 512 bits. + * @param[in] key_array_length: number of keys present. + * @param[in] key_array: set of keys to be installed. + * @param[in] pst: the Provider Session Token. + * @param[in] srm_restriction_data: optional data specifying the minimum SRM + * version. + * @param[in] license_type: specifies if the license contains content keys or + * entitlement keys. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. - * [in] enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. - * [in] enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is - * 512 bits. - * [in] key_array_length: number of keys present. - * [in] key_array: set of keys to be installed. - * [in] pst: the Provider Session Token. - * [in] srm_restriction_data: optional data specifying the minimum SRM - * version. - * [in] license_type: specifies if the license contains content keys or - * entitlement keys. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_TOO_MANY_KEYS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_LICENSE_RELOAD * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_TOO_MANY_KEYS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_LICENSE_RELOAD - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadKeys( @@ -1387,251 +1454,248 @@ OEMCryptoResult OEMCrypto_LoadKeys( OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data, OEMCrypto_LicenseType license_type); -/* - * OEMCrypto_LoadLicense +/** + * Install a set of keys for performing decryption in the current session. * - * Description: - * Install a set of keys for performing decryption in the current session. + * First, OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session + * context. * - * First, OEMCrypto shall verify the signature of the message using - * HMAC-SHA256 with the derived mac_key[server]. The signature verification - * shall use a constant-time algorithm (a signature mismatch will always take - * the same time as a successful comparison). The signature is over the - * entire message buffer starting at message with length message_length. If - * the signature verification fails, ignore all other arguments and return - * OEMCrypto_ERROR_SIGNATURE_FAILURE. Otherwise, add the keys to the session - * context. + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). * - * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey(). + * Refer to the Verification of Messages from a Server section above for more + * details. * - * Refer to the Verification of Messages from a Server section above for more - * details. + * The function ODK_ParseLicense is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseLicense is described in the document "Widevine Core + * Message Serialization". * - * The function ODK_ParseLicense is called to parse the message. If it - * returns an error, OEMCrypto shall return that error to the CDM layer. The - * function ODK_ParseLicense is described in the document "Widevine Core - * Message Serialization". + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returned by ODK_ParseLicense. * - * Below, all fields are found in the struct ODK_ParsedLicense parsed_license - * returned by ODK_ParseLicense. + * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and + * the IV given in the KeyObject. Each key control block will be decrypted + * using the first 128 bits of the corresponding content key (AES-128-CBC) + * and the IV given in the KeyObject. * - * The keys will be decrypted using the current encrypt_key (AES-128-CBC) and - * the IV given in the KeyObject. Each key control block will be decrypted - * using the first 128 bits of the corresponding content key (AES-128-CBC) - * and the IV given in the KeyObject. + * If its length is not zero, enc_mac_keys will be used to create new + * mac_keys. After all keys have been decrypted and validated, the new + * mac_keys are decrypted with the current encrypt_key and the offered IV. + * The new mac_keys replaces the current mac_keys for future signing renewal + * requests and loading renewal responses. The first 256 bits of the mac_keys + * become the mac_key[server] and the following 256 bits of the mac_keys + * become the mac_key[client]. If enc_mac_keys is null, then there will not + * be a call to OEMCrypto_LoadRenewal for this session and the current + * mac_keys may be deleted. * - * If its length is not zero, enc_mac_keys will be used to create new - * mac_keys. After all keys have been decrypted and validated, the new - * mac_keys are decrypted with the current encrypt_key and the offered IV. - * The new mac_keys replaces the current mac_keys for future signing renewal - * requests and loading renewal responses. The first 256 bits of the mac_keys - * become the mac_key[server] and the following 256 bits of the mac_keys - * become the mac_key[client]. If enc_mac_keys is null, then there will not - * be a call to OEMCrypto_LoadRenewal for this session and the current - * mac_keys may be deleted. + * If the field license_type is OEMCrypto_ContentLicense, then the fields + * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * content_key_id and content_key_data fields of the key table entry. In this + * case, entitlement key ids and entitlement key data is left blank. * - * If the field license_type is OEMCrypto_ContentLicense, then the fields - * key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * content_key_id and content_key_data fields of the key table entry. In this - * case, entitlement key ids and entitlement key data is left blank. + * If the field license_type is OEMCrypto_EntitlementLicense, then the + * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the + * entitlement_key_id and entitlement_key_data fields of the key table entry. + * In this case, content key ids and content key data will be loaded later + * with a call to OEMCrypto_LoadEntitledContentKeys(). * - * If the field license_type is OEMCrypto_EntitlementLicense, then the - * fields key_id and key_data in an OEMCrypto_KeyObject are loaded in to the - * entitlement_key_id and entitlement_key_data fields of the key table entry. - * In this case, content key ids and content key data will be loaded later - * with a call to OEMCrypto_LoadEntitledContentKeys(). + * OEMCrypto may assume that the key_id_length is at most 16. However, + * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. * - * OEMCrypto may assume that the key_id_length is at most 16. However, - * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. + * OEMCrypto shall handle multiple keys, as described in the section on + * Resource Rating Tiers in this document. * - * OEMCrypto shall handle multiple keys, as described in the section on - * Resource Rating Tiers in this document. + * After a call to OEMCrypto_LoadLicense, oemcrypto should clear the + * encrypt_key for the session. * - * After a call to OEMCrypto_LoadLicense, oemcrypto should clear the - * encrypt_key for the session. + * @verification + * The following checks should be performed. If any check fails, an error is + * returned, and none of the keys are loaded. + * 13. The signature of the message shall be computed, and the API shall + * verify the computed signature matches the signature passed in. If + * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature + * verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). + * 14. If there already is a license loaded into this session, return + * OEMCrypto_ERROR_LICENSE_RELOAD. + * 15. The enc_mac_keys substring must either have zero length, or satisfy + * the range check. I.e. (offset < message_length) && (offset + length + * <= message_length) && (offset <= offset + length), and offset + length + * does not cause an integer overflow. If it does not have zero length, + * then enc_mac_keys_iv must not have zero length, and must also satisfy + * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If + * the length is zero, then OEMCrypto may assume that the offset is also + * zero. + * 16. The API shall verify that each substring in each KeyObject points to + * a location in the message. I.e. (offset < message_length) && + * (offset + length <= message_length) && (offset <= offset + length), + * and offset + length does not cause an integer overflow, for each of + * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, + * return OEMCrypto_ERROR_INVALID_CONTEXT. + * 17. Each key's control block, after decryption, shall have a valid + * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 18. If any key control block has the Nonce_Enabled bit set, that key's + * Nonce field shall match a nonce in the cache. If not, return + * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that + * nonce from the cache. Note that all the key control blocks in a + * particular call shall have the same nonce value. + * 19. If any key control block has the Require_AntiRollback_Hardware bit + * set, and the device does not protect the usage table from rollback, + * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * 20. If the key control block has a nonzero Replay_Control, then the + * verification described below is also performed. + * 21. If the key control block has the bit SRMVersionRequired is set, then + * the verification described below is also performed. If the SRM + * requirement is not met, then the key control block's HDCP_Version + * will be changed to 0xF - local display only. + * 22. If key_array_length == 0, then return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 23. If this session is associated with a usage table entry, and that + * entry is marked as "inactive" (either kInactiveUsed or + * kInactiveUnused), then the keys are not loaded, and the error + * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. + * 24. The data in enc_mac_keys_iv is not identical to the 16 bytes before + * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. * - * Verification: - * The following checks should be performed. If any check fails, an error is - * returned, and none of the keys are loaded. - * 13. The signature of the message shall be computed, and the API shall - * verify the computed signature matches the signature passed in. If - * not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature - * verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). - * 14. If there already is a license loaded into this session, return - * OEMCrypto_ERROR_LICENSE_RELOAD. - * 15. The enc_mac_keys substring must either have zero length, or satisfy - * the range check. I.e. (offset < message_length) && (offset + length - * < message_length) && (offset < offset + length),and offset + length - * does not cause an integer overflow. If it does not have zero length, - * then enc_mac_keys_iv must not have zero length, and must also satisfy - * the range check. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. If - * the length is zero, then OEMCrypto may assume that the offset is also - * zero. - * 16. The API shall verify that each substring in each KeyObject points to - * a location in the message. I.e. (offset < message_length) && - * (offset + length < message_length) && (offset < offset + length) and - * offset + length does not cause an integer overflow, for each of - * key_id, key_data_iv, key_data, key_control_iv, key_control. If not, - * return OEMCrypto_ERROR_INVALID_CONTEXT. - * 17. Each key's control block, after decryption, shall have a valid - * verification field. If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 18. If any key control block has the Nonce_Enabled bit set, that key's - * Nonce field shall match a nonce in the cache. If not, return - * OEMCrypto_ERROR_INVALID_NONCE. If there is a match, remove that - * nonce from the cache. Note that all the key control blocks in a - * particular call shall have the same nonce value. - * 19. If any key control block has the Require_AntiRollback_Hardware bit - * set, and the device does not protect the usage table from rollback, - * then do not load the keys and return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 20. If the key control block has a nonzero Replay_Control, then the - * verification described below is also performed. - * 21. If the key control block has the bit SRMVersionRequired is set, then - * the verification described below is also performed. If the SRM - * requirement is not met, then the key control block's HDCP_Version - * will be changed to 0xF - local display only. - * 22. If key_array_length == 0, then return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 23. If this session is associated with a usage table entry, and that - * entry is marked as "inactive" (either kInactiveUsed or - * kInactiveUnused), then the keys are not loaded, and the error - * OEMCrypto_ERROR_LICENSE_INACTIVE is returned. - * 24. The data in enc_mac_keys_iv is not identical to the 16 bytes before - * enc_mac_keys. If it is, return OEMCrypto_ERROR_INVALID_CONTEXT. + * Usage Table and Provider Session Token (pst) + * The function ODK_ParseLicense takes several parameters that may need more + * explanation. + * The parameter usage_entry_present shall be set to true if a usage entry + * was created or loaded for this session. This parameter is used by + * ODK_ParseLicense for usage entry verification. + * The parameter initial_license_load shall be false if the usage entry was + * loaded. If there is no usage entry or if the usage entry was created with + * OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true. + * If a usage entry is present, then it shall be verified after the call to + * ODK_ParseLicense. + * If initial_license_load is true: + * 1. OEMCrypto shall copy the PST from the parsed license to the usage + * entry. + * 2. OEMCrypto shall verify that the server and client mac keys were + * updated by the license. The server and client mac keys shall be + * copied to the usage entry. + * If initial_license_load is false: + * 1. OEMCrypto shall verify the PST from the parsed license matches that + * in the usage entry. If not, then an error OEMCrypto_ERROR_WRONG_PST + * is returned. + * 2. OEMCrypto shall verify that the server and client mac keys were + * updated by the license. OEMCrypto shall verify that the server and + * client mac keys match those in the usage entry. If not the error + * OEMCrypto_ERROR_WRONG_KEYS is returned. + * If a key control block has a nonzero value for Replay_Control, then all + * keys in this license will have the same value for Replay_Control. In this + * case, the following additional checks are performed. + * - The substring pst must have nonzero length and must satisfy the range + * check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * - The session must be associated with a usage table entry, either + * created via OEMCrypto_CreateNewUsageEntry or loaded via + * OEMCrypto_LoadUsageEntry. + * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a + * nonce check as described above. OEMCrypto will verify that the + * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If + * an existing entry was reloaded, an error + * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. + * OEMCrypto will then copy the pst and the mac keys to the usage entry, + * and set the status to Unused. The license received time of the entry + * will be updated to the current time, and the status will be set to + * Unused. This Replay_Control prevents the license from being loaded + * more than once, and will be used for online streaming. + * - If Replay_Control is 2 = "Require existing Session Usage table entry + * or Nonce", then OEMCrypto will behave slightly differently on the + * first call to LoadKeys for this license. + * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry + * for this session, then OEMCrypto will verify the nonce for each + * key. OEMCrypto will copy the pst and mac keys to the usage + * entry. The license received time of the entry will be updated + * to the current time, and the status will be set to Unused. + * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for + * this session, then OEMCrypto will NOT verify the nonce for each + * key. Instead, it will verify that the pst passed in matches + * that in the entry. Also, the entry's mac keys will be verified + * against the current session's mac keys. This allows an offline + * license to be reloaded but maintain continuity of the playback + * times from one session to the next. + * * If the nonce is not valid and a usage entry was not loaded, the + * return error is OEMCrypto_ERROR_INVALID_NONCE. + * * If the loaded usage entry has a pst that does not match, + * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. + * * If the loaded usage entry has mac keys that do not match the + * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. + * Note: If LoadKeys updates the mac keys, then the new updated mac keys will + * be used with the Usage Entry -- i.e. the new keys are stored in the + * usage table when creating a new entry, or the new keys are verified + * against those in the usage table if there is an existing entry. If + * LoadKeys does not update the mac keys, the existing session mac keys are + * used. + * Sessions that are associated with an entry will need to be able to update + * and verify the status of the entry, and the time stamps in the entry. + * Devices that do not support the Usage Table will return + * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. + * SRM Restriction Data + * If any key control block has the flag SRMVersionRequired set, then the + * following verification is also performed. + * 4. The substring srm_restriction_data must have nonzero length and must + * satisfy the range check described above. If not, return + * OEMCrypto_ERROR_INVALID_CONTEXT. + * 5. The first 8 bytes of srm_restriction_data must match the string + * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. + * 6. The next 4 bytes of srm_restriction_data will be converted from + * network byte order. If the current SRM installed on the device has a + * version number less than this, then the SRM requirement is not met. + * If the device does not support SRM files, or OEMCrypto cannot + * determine the current SRM version number, then the SRM requirement is + * not met. + * Note: if the current SRM version requirement is not met, LoadKeys will + * still succeed and the keys will be loaded. However, those keys with the + * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - + * local display only. Any future call to SelectKey for these keys while + * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP + * at that time. * - * Usage Table and Provider Session Token (pst) - * The function ODK_ParseLicense takes several parameters that may need more - * explanation. - * The parameter usage_entry_present shall be set to true if a usage entry - * was created or loaded for this session. This parameter is used by - * ODK_ParseLicense for usage entry verification. - * The parameter initial_license_load shall be false if the usage entry was - * loaded. If there is no usage entry or if the usage entry was created with - * OEMCrypto_CreateNewUsageEntry, then initial_license_load shall be true. - * If a usage entry is present, then it shall be verified after the call to - * ODK_ParseLicense. - * If initial_license_load is true: - * 1. OEMCrypto shall copy the PST from the parsed license to the usage - * entry. - * 2. OEMCrypto shall verify that the server and client mac keys were - * updated by the license. The server and client mac keys shall be - * copied to the usage entry. - * If initial_license_load is false: - * 1. OEMCrypto shall verify the PST from the parsed license matches that - * in the usage entry. If not, then an error OEMCrypto_ERROR_WRONG_PST - * is returned. - * 2. OEMCrypto shall verify that the server and client mac keys were - * updated by the license. OEMCrypto shall verify that the server and - * client mac keys match those in the usage entry. If not the error - * OEMCrypto_ERROR_WRONG_KEYS is returned. - * If a key control block has a nonzero value for Replay_Control, then all - * keys in this license will have the same value for Replay_Control. In this - * case, the following additional checks are performed. - * - The substring pst must have nonzero length and must satisfy the range - * check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * - The session must be associated with a usage table entry, either - * created via OEMCrypto_CreateNewUsageEntry or loaded via - * OEMCrypto_LoadUsageEntry. - * - If Replay_Control is 1 = Nonce_Required, then OEMCrypto will perform a - * nonce check as described above. OEMCrypto will verify that the - * usage entry is newly created with OEMCrypto_CreateNewUsageEntry. If - * an existing entry was reloaded, an error - * OEMCrypto_ERROR_INVALID_CONTEXT is returned and no keys are loaded. - * OEMCrypto will then copy the pst and the mac keys to the usage entry, - * and set the status to Unused. This Replay_Control prevents the - * license from being loaded more than once, and will be used for online - * streaming. - * - If Replay_Control is 2 = "Require existing Session Usage table entry - * or Nonce", then OEMCrypto will behave slightly differently on the - * first call to LoadKeys for this license. - * * If the usage entry was created with OEMCrypto_CreateNewUsageEntry - * for this session, then OEMCrypto will verify the nonce for each - * key. OEMCrypto will copy the pst and mac keys to the usage - * entry. The license received time of the entry will be updated - * to the current time, and the status will be set to Unused. - * * If the usage entry was loaded with OEMCrypto_LoadUsageEntry for - * this session, then OEMCrypto will NOT verify the nonce for each - * key. Instead, it will verify that the pst passed in matches - * that in the entry. Also, the entry's mac keys will be verified - * against the current session's mac keys. This allows an offline - * license to be reloaded but maintain continuity of the playback - * times from one session to the next. - * * If the nonce is not valid and a usage entry was not loaded, the - * return error is OEMCrypto_ERROR_INVALID_NONCE. - * * If the loaded usage entry has a pst that does not match, - * OEMCrypto returns the error OEMCrypto_ERROR_WRONG_PST. - * * If the loaded usage entry has mac keys that do not match the - * license, OEMCrypto returns the error OEMCrypto_ERROR_WRONG_KEYS. - * Note: If LoadKeys updates the mac keys, then the new updated mac keys will - * be used with the Usage Entry -- i.e. the new keys are stored in the - * usage table when creating a new entry, or the new keys are verified - * against those in the usage table if there is an existing entry. If - * LoadKeys does not update the mac keys, the existing session mac keys are - * used. - * Sessions that are associated with an entry will need to be able to update - * and verify the status of the entry, and the time stamps in the entry. - * Devices that do not support the Usage Table will return - * OEMCrypto_ERROR_INVALID_CONTEXT if the Replay_Control is nonzero. - * SRM Restriction Data - * If any key control block has the flag SRMVersionRequired set, then the - * following verification is also performed. - * 4. The substring srm_restriction_data must have nonzero length and must - * satisfy the range check described above. If not, return - * OEMCrypto_ERROR_INVALID_CONTEXT. - * 5. The first 8 bytes of srm_restriction_data must match the string - * "HDCPDATA". If not, return OEMCrypto_ERROR_INVALID_CONTEXT. - * 6. The next 4 bytes of srm_restriction_data will be converted from - * network byte order. If the current SRM installed on the device has a - * version number less than this, then the SRM requirement is not met. - * If the device does not support SRM files, or OEMCrypto cannot - * determine the current SRM version number, then the SRM requirement is - * not met. - * Note: if the current SRM version requirement is not met, LoadKeys will - * still succeed and the keys will be loaded. However, those keys with the - * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - - * local display only. Any future call to SelectKey for these keys while - * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP - * at that time. + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing data. + * @param[in] message_length: length of the message, in bytes. + * @param[in] core_message_length: length of the core submessage, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing data. - * [in] message_length: length of the message, in bytes. - * [in] core_message_length: length of the core submessage, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. + * @retval OEMCrypto_SUCCESS success OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_TOO_MANY_KEYS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_LICENSE_RELOAD * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_TOO_MANY_KEYS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_LICENSE_RELOAD - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, @@ -1641,65 +1705,60 @@ OEMCryptoResult OEMCrypto_LoadLicense(OEMCrypto_SESSION session, const uint8_t* signature, size_t signature_length); -/* - * OEMCrypto_LoadEntitledContentKeys +/** + * Load content keys into a session which already has entitlement keys + * loaded. This function will only be called for a session after a call to + * OEMCrypto_LoadKeys with the parameter type license_type equal to + * OEMCrypto_EntitlementLicense. This function may be called multiple times + * for the same session. * - * Description: - * Load content keys into a session which already has entitlement keys - * loaded. This function will only be called for a session after a call to - * OEMCrypto_LoadKeys with the parameter type license_type equal to - * OEMCrypto_EntitlementLicense. This function may be called multiple times - * for the same session. + * If the session does not have license_type equal to + * OEMCrypto_EntitlementLicense, return OEMCrypto_ERROR_INVALID_CONTEXT and + * perform no work. * - * If the session does not have license_type equal to - * OEMCrypto_EntitlementLicense, return OEMCrypto_ERROR_INVALID_CONTEXT and - * perform no work. + * For each key object in key_array, OEMCrypto shall look up the entry in the + * key table with the corresponding entitlement_key_id. * - * For each key object in key_array, OEMCrypto shall look up the entry in the - * key table with the corresponding entitlement_key_id. + * 1. If no entry is found, return OEMCrypto_KEY_NOT_ENTITLED. + * 2. If the entry already has a content_key_id and content_key_data, that + * id and data are erased. + * 3. The content_key_id from the key_array is copied to the entry's + * content_key_id. + * 4. The content_key_data decrypted using the entitlement_key_data as a + * key for AES-256-CBC with an IV of content_key_data_iv. Wrapped + * content is padded using PKCS#7 padding. Notice that the entitlement + * key will be an AES 256 bit key. The clear content key data will be + * stored in the entry's content_key_data. + * Entries in the key table that do not correspond to anything in the + * key_array are not modified or removed. * - * 1. If no entry is found, return OEMCrypto_KEY_NOT_ENTITLED. - * 2. If the entry already has a content_key_id and content_key_data, that - * id and data are erased. - * 3. The content_key_id from the key_array is copied to the entry's - * content_key_id. - * 4. The content_key_data decrypted using the entitlement_key_data as a - * key for AES-256-CBC with an IV of content_key_data_iv. Wrapped - * content is padded using PKCS#7 padding. Notice that the entitlement - * key will be an AES 256 bit key. The clear content key data will be - * stored in the entry's content_key_data. - * Entries in the key table that do not correspond to anything in the - * key_array are not modified or removed. + * For devices that use a hardware key ladder, it may be more convenient to + * store the encrypted content key data in the key table, and decrypt it when + * the function SelectKey is called. * - * For devices that use a hardware key ladder, it may be more convenient to - * store the encrypted content key data in the key table, and decrypt it when - * the function SelectKey is called. + * @param[in] session: handle for the session to be used. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] key_array_length: number of keys present. + * @param[in] key_array: set of key updates. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] key_array_length: number of keys present. - * [in] key_array: set of key updates. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_KEY_NOT_ENTITLED + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_KEY_NOT_ENTITLED - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 14. */ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( @@ -1707,100 +1766,95 @@ OEMCryptoResult OEMCrypto_LoadEntitledContentKeys( size_t key_array_length, const OEMCrypto_EntitledContentKeyObject* key_array); -/* - * OEMCrypto_RefreshKeys +/** + * Updates the license clock values to allow playback to continue. This + * function is being deprecated and is only used for version v15 licenses -- + * i.e. offline license saved before an update or licenses from a server that + * has not update to the v16 license server SDK. * - * Description: - * Updates the license clock values to allow playback to continue. This - * function is being deprecated and is only used for version v15 licenses -- - * i.e. offline license saved before an update or licenses from a server that - * has not update to the v16 license server SDK. + * OEMCrypto shall compute the signature of the message using + * mac_key[server], and shall verify the computed signature matches the + * signature passed in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The + * signature verification shall use a constant-time algorithm (a signature + * mismatch will always take the same time as a successful comparison). * - * OEMCrypto shall compute the signature of the message using - * mac_key[server], and shall verify the computed signature matches the - * signature passed in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The - * signature verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). + * The key control from the first OEMCrypto_KeyRefreshObject in the key_array + * shall be extracted. If it is encrypted, as described below, it shall be + * decrypted. The duration from the key control shall be extracted and + * converted to host byte order. This duration shall be passed to the + * function ODK_RefreshV15Values as the parameter new_key_duration. * - * The key control from the first OEMCrypto_KeyRefreshObject in the key_array - * shall be extracted. If it is encrypted, as described below, it shall be - * decrypted. The duration from the key control shall be extracted and - * converted to host byte order. This duration shall be passed to the - * function ODK_RefreshV15Values as the parameter new_key_duration. + * If the KeyRefreshObject's key_control_iv has zero length, then the + * key_control is not encrypted. If the key_control_iv is specified, then + * key_control is encrypted with the first 128 bits of the corresponding + * content key. * - * If the KeyRefreshObject's key_control_iv has zero length, then the - * key_control is not encrypted. If the key_control_iv is specified, then - * key_control is encrypted with the first 128 bits of the corresponding - * content key. + * If the KeyRefreshObject's key_id has zero length, then it is an error for + * the key_control_iv to have nonzero length. OEMCrypto shall return an error + * of OEMCrypto_ERROR_INVALID_CONTEXT. * - * If the KeyRefreshObject's key_id has zero length, then it is an error for - * the key_control_iv to have nonzero length. OEMCrypto shall return an error - * of OEMCrypto_ERROR_INVALID_CONTEXT. + * If the session's license_type is OEMCrypto_ContentLicense, and the + * KeyRefreshObject's key_id is not null, then the entry in the keytable with + * the matching content_key_id is used. * - * If the session's license_type is OEMCrypto_ContentLicense, and the - * KeyRefreshObject's key_id is not null, then the entry in the keytable with - * the matching content_key_id is used. + * If the session's license_type is OEMCrypto_EntitlementLicense, and the + * KeyRefreshObject's key_id is not null, then the entry in the keytable with + * the matching entitlment_key_id is used. * - * If the session's license_type is OEMCrypto_EntitlementLicense, and the - * KeyRefreshObject's key_id is not null, then the entry in the keytable with - * the matching entitlment_key_id is used. + * The function ODK_RefreshV15Values shall be called to update the clock + * values. See the document "Widevine Core Message Serialization" for the + * documentation of the ODK library functions. * - * The function ODK_RefreshV15Values shall be called to update the clock - * values. See the document "Widevine Core Message Serialization" for the - * documentation of the ODK library functions. + * If ODK_RefreshV15Values returns * - * If ODK_RefreshV15Values returns + * - ODK_SET_TIMER: Success. The timer should be reset to the specified + * timer value. + * - ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is + * allowed. + * - ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. + * - ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is + * rejected. Return this error + * - Any other error - OEMCrypto shall pass any other error up to the + * caller of OEMCrypto_RefreshKeys. * - * - ODK_SET_TIMER: Success. The timer should be reset to the specified - * timer value. - * - ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is - * allowed. - * - ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. - * - ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is - * rejected. Return this error - * - Any other error - OEMCrypto shall pass any other error up to the - * caller of OEMCrypto_RefreshKeys. + * NOTE: OEMCrypto_LoadKeys() must be called first to load the keys into the + * session. * - * NOTE: OEMCrypto_LoadKeys() must be called first to load the keys into the - * session. + * @param[in] session: handle for the session to be used. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. + * @param[in] num_keys: number of keys present. + * @param[in] key_array: set of key updates. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. - * [in] num_keys: number of keys present. - * [in] key_array: set of key updates. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_NO_CONTENT_KEY + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_NO_CONTENT_KEY - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_RefreshKeys( @@ -1808,78 +1862,73 @@ OEMCryptoResult OEMCrypto_RefreshKeys( const uint8_t* signature, size_t signature_length, size_t num_keys, const OEMCrypto_KeyRefreshObject* key_array); -/* - * OEMCrypto_LoadRenewal +/** + * Updates the clock values and resets the renewal timer for the current + * session. * - * Description: - * Updates the clock values and resets the renewal timer for the current - * session. + * OEMCrypto shall verify the signature of the entire message using the + * session's renewal mac key for the server. The entire message is the buffer + * starting at message with length message_length. If the signature does not + * match, OEMCrypto returns OEMCrypto_ERROR_SIGNATURE_FAILURE. * - * OEMCrypto shall verify the signature of the entire message using the - * session's renewal mac key for the server. The entire message is the buffer - * starting at message with length message_length. If the signature does not - * match, OEMCrypto returns OEMCrypto_ERROR_SIGNATURE_FAILURE. + * OEMCrypto shall verify that nonce_values.api_major_version is 16. If not, + * return the error OEMCrypto_ERROR_INVALID_CONTEXT. Legacy licenses will use + * the function OEMCrypto_RefreshKeys instead of OEMCrypto_LoadRenewal. * - * OEMCrypto shall verify that nonce_values.api_major_version is 16. If not, - * return the error OEMCrypto_ERROR_INVALID_CONTEXT. Legacy licenses will use - * the function OEMCrypto_RefreshKeys instead of OEMCrypto_LoadRenewal. + * If the signature passes, OEMCrypto shall use the function + * ODK_ParseRenewal, as described in the document "Widevine Core Message + * Serialization" to parse and verify the message. If ODK_ParseRenewal + * returns an error OEMCrypto returns the error to the CDM layer. * - * If the signature passes, OEMCrypto shall use the function - * ODK_ParseRenewal, as described in the document "Widevine Core Message - * Serialization" to parse and verify the message. If ODK_ParseRenewal - * returns an error OEMCrypto returns the error to the CDM layer. + * The function ODK_ParseRenewal updates the clock values for the session, + * and may return ODK_SET_TIMER, ODK_DISABLE_TIMER or ODK_TIMER_EXPIRED on + * success. These values shall be handled by OEMCrypto, as discussed in the + * document "License Duration and Renewal". * - * The function ODK_ParseRenewal updates the clock values for the session, - * and may return ODK_SET_TIMER, ODK_DISABLE_TIMER or ODK_TIMER_EXPIRED on - * success. These values shall be handled by OEMCrypto, as discussed in the - * document "License Duration and Renewal". + * NOTE: OEMCrypto_LoadLicense() must be called first to load the keys into + * the session. * - * NOTE: OEMCrypto_LoadLicense() must be called first to load the keys into - * the session. - * - * Verification: + * @verification * The signature of the message shall be computed using mac_key[server], and * the API shall verify the computed signature matches the signature passed * in. If not, return OEMCrypto_ERROR_SIGNATURE_FAILURE. The signature * verification shall use a constant-time algorithm (a signature mismatch * will always take the same time as a successful comparison). * - * Parameters: - * [in] session: handle for the session to be used. - * [in] message: pointer to memory containing message to be verified. - * [in] message_length: length of the message, in bytes. - * [in] core_message_length: length of the core submessage, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. + * @param[in] session: handle for the session to be used. + * @param[in] message: pointer to memory containing message to be verified. + * @param[in] message_length: length of the message, in bytes. + * @param[in] core_message_length: length of the core submessage, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * ODK_STALE_RENEWAL + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval ODK_STALE_RENEWAL * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, @@ -1889,23 +1938,20 @@ OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, const uint8_t* signature, size_t signature_length); -/* - * OEMCrypto_QueryKeyControl +/** + * Returns the decrypted key control block for the given content_key_id. This + * function is for application developers to debug license server and key + * timelines. It only returns a key control block if LoadKeys was successful, + * otherwise it returns OEMCrypto_ERROR_NO_CONTENT_KEY. The developer of the + * OEMCrypto library must be careful that the keys themselves are not + * accidentally revealed. * - * Description: - * Returns the decrypted key control block for the given content_key_id. This - * function is for application developers to debug license server and key - * timelines. It only returns a key control block if LoadKeys was successful, - * otherwise it returns OEMCrypto_ERROR_NO_CONTENT_KEY. The developer of the - * OEMCrypto library must be careful that the keys themselves are not - * accidentally revealed. + * Note: returns control block in original, network byte order. If OEMCrypto + * converts fields to host byte order internally for storage, it should + * convert them back. Since OEMCrypto might not store the nonce or validation + * fields, values of 0 may be used instead. * - * Note: returns control block in original, network byte order. If OEMCrypto - * converts fields to host byte order internally for storage, it should - * convert them back. Since OEMCrypto might not store the nonce or validation - * fields, values of 0 may be used instead. - * - * Verification: + * @verification * The following checks should be performed. * 1. If key_id is null, return OEMCrypto_ERROR_INVALID_CONTEXT. * 2. If key_control_block_length is null, return @@ -1917,30 +1963,29 @@ OEMCryptoResult OEMCrypto_LoadRenewal(OEMCrypto_SESSION session, * 5. If the specified key has not been loaded, return * OEMCrypto_ERROR_NO_CONTENT_KEY. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] content_key_id: The unique id of the key of interest. - * [in] content_key_id_length: The length of key_id, in bytes. From 1 to 16, - * inclusive. - * [out] key_control_block: A caller-owned buffer. - * [in/out] key_control_block_length. The length of key_control_block buffer. + * @param[in] session: handle for the session to be used. + * @param[in] content_key_id: The unique id of the key of interest. + * @param[in] content_key_id_length: The length of key_id, in bytes. From 1 to + * 16, inclusive. + * @param[out] key_control_block: A caller-owned buffer. + * @param[in,out] key_control_block_length. The length of key_control_block + * buffer. * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 10. */ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, @@ -1949,88 +1994,86 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, uint8_t* key_control_block, size_t* key_control_block_length); -/* - * OEMCrypto_SelectKey +/// @} + +/// @addtogroup decryption +/// @{ + +/** + * Select a content key and install it in the hardware key ladder for + * subsequent decryption operations (OEMCrypto_DecryptCENC()) for this + * session. The specified key must have been previously "installed" via + * OEMCrypto_LoadKeys(), OEMCrypto_LoadLicense, or + * OEMCrypto_LoadEntitledContentKeys(). * - * Description: - * Select a content key and install it in the hardware key ladder for - * subsequent decryption operations (OEMCrypto_DecryptCENC()) for this - * session. The specified key must have been previously "installed" via - * OEMCrypto_LoadKeys(), OEMCrypto_LoadLicense, or - * OEMCrypto_LoadEntitledContentKeys(). + * A key control block is associated with the key and the session, and is + * used to configure the session context. The Key Control data is documented + * in "Key Control Block Definition". * - * A key control block is associated with the key and the session, and is - * used to configure the session context. The Key Control data is documented - * in "Key Control Block Definition". + * Step 1: Lookup the content key data via the offered key_id. The key data + * includes the key value, and the key control block. * - * Step 1: Lookup the content key data via the offered key_id. The key data - * includes the key value, and the key control block. + * Step 2: Latch the content key into the hardware key ladder. Set permission + * flags based on the key's control block. * - * Step 2: Latch the content key into the hardware key ladder. Set permission - * flags based on the key's control block. + * Step 3: use the latched content key to decrypt (AES-128-CTR or + * AES-128-CBC) buffers passed in via OEMCrypto_DecryptCENC(). If the key is + * 256 bits it will be used for OEMCrypto_Generic_Sign or + * OEMCrypto_Generic_Verify as specified in the key control block. If the key + * will be used for OEMCrypto_Generic_Encrypt or OEMCrypto_Generic_Decrypt + * then the cipher mode will always be OEMCrypto_CipherMode_CBC. Continue to + * use this key for this session until OEMCrypto_SelectKey() is called again, + * or until OEMCrypto_CloseSession() is called. * - * Step 3: use the latched content key to decrypt (AES-128-CTR or - * AES-128-CBC) buffers passed in via OEMCrypto_DecryptCENC(). If the key is - * 256 bits it will be used for OEMCrypto_Generic_Sign or - * OEMCrypto_Generic_Verify as specified in the key control block. If the key - * will be used for OEMCrypto_Generic_Encrypt or OEMCrypto_Generic_Decrypt - * then the cipher mode will always be OEMCrypto_CipherMode_CBC. Continue to - * use this key for this session until OEMCrypto_SelectKey() is called again, - * or until OEMCrypto_CloseSession() is called. + * @verification + * 1. If the key id is not found in the keytable for this session, then the + * key state is not changed and OEMCrypto shall return + * OEMCrypto_ERROR_NO_CONTENT_KEY. + * 2. If the key control block has the bit Disable_Analog_Output set, then + * the device should disable analog video output. If the device has + * analog video output that cannot be disabled, then the key is not + * selected, and OEMCrypto_ERROR_ANALOG_OUTPUT is returned. This step is + * optional -- SelectKey may return OEMCrypto_SUCCESS and delay the + * error until a call to OEMCrypto_DecryptCENC. + * 3. If the key control block has HDCP required, and the device cannot + * enforce HDCP, then the key is not selected, and + * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. This step is optional + * -- SelectKey may return OEMCrypto_SUCCESS and delay the error until a + * call to OEMCrypto_DecryptCENC. + * 4. If the key control block has a nonzero value for HDCP_Version, and + * the device cannot enforce at least that version of HDCP, then the key + * is not selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. * - * Verification: - * 1. If the key id is not found in the keytable for this session, then the - * key state is not changed and OEMCrypto shall return - * OEMCrypto_ERROR_NO_CONTENT_KEY. - * 2. If the key control block has the bit Disable_Analog_Output set, then - * the device should disable analog video output. If the device has - * analog video output that cannot be disabled, then the key is not - * selected, and OEMCrypto_ERROR_ANALOG_OUTPUT is returned. This step is - * optional -- SelectKey may return OEMCrypto_SUCCESS and delay the - * error until a call to OEMCrypto_DecryptCENC. - * 3. If the key control block has HDCP required, and the device cannot - * enforce HDCP, then the key is not selected, and - * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. This step is optional - * -- SelectKey may return OEMCrypto_SUCCESS and delay the error until a - * call to OEMCrypto_DecryptCENC. - * 4. If the key control block has a nonzero value for HDCP_Version, and - * the device cannot enforce at least that version of HDCP, then the key - * is not selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. + * @param[in] session: crypto session identifier. + * @param[in] content_key_id: pointer to the content Key ID. + * @param[in] content_key_id_length: length of the content Key ID, in bytes. + * From 1 to 16, inclusive. + * @param[in] cipher_mode: whether the key should be prepared for CTR mode or + * CBC mode when used in later calls to DecryptCENC. This should be ignored + * when the key is used for Generic Crypto calls. * - * Parameters: - * [in] session: crypto session identifier. - * [in] content_key_id: pointer to the content Key ID. - * [in] content_key_id_length: length of the content Key ID, in bytes. From - * 1 to 16, inclusive. - * [in] cipher_mode: whether the key should be prepared for CTR mode or CBC - * mode when used in later calls to DecryptCENC. This should be ignored - * when the key is used for Generic Crypto calls. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED if the session's timer has expired + * @retval OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key + * @retval OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key + * @retval OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input + * @retval OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_ANALOG_OUTPUT + * @retval OEMCrypto_ERROR_INSUFFICIENT_HDCP + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - if the session's timer has expired - * OEMCrypto_ERROR_INVALID_SESSION crypto session ID invalid or not open - * OEMCrypto_ERROR_NO_DEVICE_KEY failed to decrypt device key - * OEMCrypto_ERROR_NO_CONTENT_KEY failed to decrypt content key - * OEMCrypto_ERROR_CONTROL_INVALID invalid or unsupported control input - * OEMCrypto_ERROR_KEYBOX_INVALID cannot decrypt and read from Keybox - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_ANALOG_OUTPUT - * OEMCrypto_ERROR_INSUFFICIENT_HDCP - * OEMCrypto_ERROR_NO_CONTENT_KEY - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, @@ -2038,365 +2081,359 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, size_t content_key_id_length, OEMCryptoCipherMode cipher_mode); -/* - * OEMCrypto_DecryptCENC +/** + * Decrypts or copies a series of input payloads into output buffers using + * the session context indicated by the session parameter. The input payload + * is delivered in the form of samples. The samples are subdivided into + * subsamples. "Samples" and "subsamples" are defined as in the ISO Common + * Encryption standard (ISO/IEC 23001-7:2016). The samples parameter contains + * a list of samples, each of which has its own input and output buffers. + * Each sample contains a buffers field that contains the input and output + * buffers in its input_data and output fields, respectively. * - * Description: - * Decrypts or copies a series of input payloads into output buffers using - * the session context indicated by the session parameter. The input payload - * is delivered in the form of samples. The samples are subdivided into - * subsamples. "Samples" and "subsamples" are defined as in the ISO Common - * Encryption standard (ISO/IEC 23001-7:2016). The samples parameter contains - * a list of samples, each of which has its own input and output buffers. - * Each sample contains a buffers field that contains the input and output - * buffers in its input_data and output fields, respectively. + * Each sample contains an array of subsample descriptions in its subsamples + * field. Each subsample is defined as a number of clear bytes followed by a + * number of encrypted bytes. Subsamples are consecutive inside the sample; + * the clear bytes of the second subsample begin immediately after the + * encrypted bytes of the first subsample. This follows the definition in the + * ISO-CENC standard. * - * Each sample contains an array of subsample descriptions in its subsamples - * field. Each subsample is defined as a number of clear bytes followed by a - * number of encrypted bytes. Subsamples are consecutive inside the sample; - * the clear bytes of the second subsample begin immediately after the - * encrypted bytes of the first subsample. This follows the definition in the - * ISO-CENC standard. + * Decryption mode is AES-128-CTR or AES-128-CBC depending on the value of + * cipher_mode previously passed in to OEMCrypto_SelectKey. For the encrypted + * portion of subsamples, the content key associated with the session is + * latched in the active hardware key ladder and is used for the decryption + * operation. For the clear portion of subsamples, the data is simply copied. * - * Decryption mode is AES-128-CTR or AES-128-CBC depending on the value of - * cipher_mode previously passed in to OEMCrypto_SelectKey. For the encrypted - * portion of subsamples, the content key associated with the session is - * latched in the active hardware key ladder and is used for the decryption - * operation. For the clear portion of subsamples, the data is simply copied. + * After decryption, all the input_data bytes are copied to the location + * described by the output field. The output field is an + * OEMCrypto_DestBufferDesc, which could be one of: * - * After decryption, all the input_data bytes are copied to the location - * described by the output field. The output field is an - * OEMCrypto_DestBufferDesc, which could be one of: + * 1. The structure OEMCrypto_DestBufferDesc contains a pointer to a clear + * text buffer. The OEMCrypto library shall verify that key control + * allows data to be returned in clear text. If it is not authorized, + * this method should return an error. + * 2. The structure OEMCrypto_DestBufferDesc contains a handle to a secure + * buffer. + * 3. The structure OEMCrypto_DestBufferDesc indicates that the data should + * be sent directly to the decoder and renderer. + * Depending on your platform's needs, you may not need to support all three + * of these options. * - * 1. The structure OEMCrypto_DestBufferDesc contains a pointer to a clear - * text buffer. The OEMCrypto library shall verify that key control - * allows data to be returned in clear text. If it is not authorized, - * this method should return an error. - * 2. The structure OEMCrypto_DestBufferDesc contains a handle to a secure - * buffer. - * 3. The structure OEMCrypto_DestBufferDesc indicates that the data should - * be sent directly to the decoder and renderer. - * Depending on your platform's needs, you may not need to support all three - * of these options. + * SINGLE-SAMPLE DECRYPTION AND SINGLE-SUBSAMPLE DECRYPTION: * - * SINGLE-SAMPLE DECRYPTION AND SINGLE-SUBSAMPLE DECRYPTION: + * If the OEMCrypto implementation is not able to handle the amount of + * samples and subsamples passed into it, it should return + * OEMCrypto_ERROR_BUFFER_TOO_LARGE, in which case the CDM can respond by + * breaking the samples up into smaller pieces and trying to decrypt each of + * them individually. It is possible that the CDM will break the samples + * array up into pieces that are still too large, in which case OEMCrypto may + * return OEMCrypto_ERROR_BUFFER_TOO_LARGE again. * - * If the OEMCrypto implementation is not able to handle the amount of - * samples and subsamples passed into it, it should return - * OEMCrypto_ERROR_BUFFER_TOO_LARGE, in which case the CDM can respond by - * breaking the samples up into smaller pieces and trying to decrypt each of - * them individually. It is possible that the CDM will break the samples - * array up into pieces that are still too large, in which case OEMCrypto may - * return OEMCrypto_ERROR_BUFFER_TOO_LARGE again. + * If the OEMCrypto implementation cannot handle multiple samples at once, it + * may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any time it receives more than + * one sample in a single call to OEMCrypto_DecryptCENC. * - * If the OEMCrypto implementation cannot handle multiple samples at once, it - * may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any time it receives more than - * one sample in a single call to OEMCrypto_DecryptCENC. + * Similarly, if the OEMCrypto implementation cannot handle multiple + * subsamples at once, it may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any + * time it receives more than one subsample in a single call to + * OEMCrypto_DecryptCENC. * - * Similarly, if the OEMCrypto implementation cannot handle multiple - * subsamples at once, it may return OEMCrypto_ERROR_BUFFER_TOO_LARGE any - * time it receives more than one subsample in a single call to - * OEMCrypto_DecryptCENC. + * The exact way that the CDM code breaks up the samples array is not + * guaranteed by this specification. The CDM may break down the array of + * samples into many arrays each containing one sample. The CDM may break + * down samples into subsamples and pass individual subsamples into + * OEMCrypto, just like in OEMCrypto v15. The CDM may break down individual + * subsamples into smaller subsamples, just like in OEMCrypto v15. * - * The exact way that the CDM code breaks up the samples array is not - * guaranteed by this specification. The CDM may break down the array of - * samples into many arrays each containing one sample. The CDM may break - * down samples into subsamples and pass individual subsamples into - * OEMCrypto, just like in OEMCrypto v15. The CDM may break down individual - * subsamples into smaller subsamples, just like in OEMCrypto v15. + * If OEMCrypto requests that the CDM break samples into subsamples, the + * "samples" passed into OEMCrypto_DecryptCENC will no longer be full + * samples. When a full sample is passed into OEMCrypto_DecryptCENC, the + * first subsample in the subsample array will have the + * OEMCrypto_FirstSubsample flag set in its subsample_flags field and the + * last subsample array will have the OEMCrypto_LastSubsample flag set in its + * subsample_flags field. If this is not the case, OEMCrypto will need to + * accumulate more subsamples from successive calls to OEMCrypto_DecryptCENC + * to receive the full sample. * - * If OEMCrypto requests that the CDM break samples into subsamples, the - * "samples" passed into OEMCrypto_DecryptCENC will no longer be full - * samples. When a full sample is passed into OEMCrypto_DecryptCENC, the - * first subsample in the subsample array will have the - * OEMCrypto_FirstSubsample flag set in its subsample_flags field and the - * last subsample array will have the OEMCrypto_LastSubsample flag set in its - * subsample_flags field. If this is not the case, OEMCrypto will need to - * accumulate more subsamples from successive calls to OEMCrypto_DecryptCENC - * to receive the full sample. + * The first subsample in the sample will always have + * OEMCrypto_FirstSubsample set and the last subsample will always have the + * OEMCrypto_LastSubsample flag set, even if those subsamples are passed in + * separate calls to OEMCrypto_DecryptCENC. This is the same as in OEMCrypto + * v15. The decrypted data will not be used until after the subsample with + * the flag OEMCrypto_LastSubsample has been sent to OEMCrypto. This can be + * relied on by OEMCrypto for optimization by not doing decrypt until the + * last subsample has been received. However, a device that can do decrypt of + * more than one subsample at a time will always have better performance if + * it can receive those subsamples in one OEMCrypto_Decrypt call rather than + * as individual subsamples. * - * The first subsample in the sample will always have - * OEMCrypto_FirstSubsample set and the last subsample will always have the - * OEMCrypto_LastSubsample flag set, even if those subsamples are passed in - * separate calls to OEMCrypto_DecryptCENC. This is the same as in OEMCrypto - * v15. The decrypted data will not be used until after the subsample with - * the flag OEMCrypto_LastSubsample has been sent to OEMCrypto. This can be - * relied on by OEMCrypto for optimization by not doing decrypt until the - * last subsample has been received. However, a device that can do decrypt of - * more than one subsample at a time will always have better performance if - * it can receive those subsamples in one OEMCrypto_Decrypt call rather than - * as individual subsamples. + * Although the exact way that the CDM code breaks up the samples array when + * it receives OEMCrypto_ERROR_BUFFER_TOO_LARGE is not guaranteed by this + * specification, here is a sample way it might work: * - * Although the exact way that the CDM code breaks up the samples array when - * it receives OEMCrypto_ERROR_BUFFER_TOO_LARGE is not guaranteed by this - * specification, here is a sample way it might work: + * 1. It tries to pass the array of samples to OEMCrypto_DecryptCENC. + * 2. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to + * pass each sample individually into OEMCrypto_DecryptCENC. + * 3. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to + * pass the clear and encrypted parts of each subsample individually + * into OEMCrypto_DecryptCENC. At this point, (and in the subsequent + * steps) it is replicating the behavior of OEMCrypto v15 and lower. + * 4. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it breaks each + * piece of a subsample into smaller pieces, down to the minimum + * subsample size required by the device's resource rating tier. It + * passes these pieces into OEMCrypto_DecryptCENC. + * 5. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, the device has + * failed to meet its resource rating tier requirements. It returns an + * error. + * Because this process requires a lot of back-and-forth between the CDM and + * OEMCrypto, partners are strongly recommended to support decrypting full + * samples or even multiple samples in their OEMCrypto implementation. * - * 1. It tries to pass the array of samples to OEMCrypto_DecryptCENC. - * 2. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to - * pass each sample individually into OEMCrypto_DecryptCENC. - * 3. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it tries to - * pass the clear and encrypted parts of each subsample individually - * into OEMCrypto_DecryptCENC. At this point, (and in the subsequent - * steps) it is replicating the behavior of OEMCrypto v15 and lower. - * 4. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, it breaks each - * piece of a subsample into smaller pieces, down to the minimum - * subsample size required by the device's resource rating tier. It - * passes these pieces into OEMCrypto_DecryptCENC. - * 5. If OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE, the device has - * failed to meet its resource rating tier requirements. It returns an - * error. - * Because this process requires a lot of back-and-forth between the CDM and - * OEMCrypto, partners are strongly recommended to support decrypting full - * samples or even multiple samples in their OEMCrypto implementation. + * ISO-CENC SCHEMES: * - * ISO-CENC SCHEMES: + * The ISO Common Encryption standard (ISO/IEC 23001-7:2016) defines four + * "schemes" that may be used to encrypt content: 'cenc', 'cens', 'cbc1', and + * 'cbcs'. Starting with v16, OEMCrypto only supports 'cenc' and 'cbcs'. The + * schemes 'cens' and 'cbc1' are not supported. * - * The ISO Common Encryption standard (ISO/IEC 23001-7:2016) defines four - * "schemes" that may be used to encrypt content: 'cenc', 'cens', 'cbc1', and - * 'cbcs'. Starting with v16, OEMCrypto only supports 'cenc' and 'cbcs'. The - * schemes 'cens' and 'cbc1' are not supported. + * The decryption mode, either OEMCrypto_CipherMode_CTR or + * OEMCrypto_CipherMode_CBC, was already specified in the call to + * OEMCrypto_SelectKey. The encryption pattern is specified by the fields in + * the parameter pattern. A description of partial encryption patterns for + * 'cbcs' can be found in the ISO-CENC standard, section 10.4. * - * The decryption mode, either OEMCrypto_CipherMode_CTR or - * OEMCrypto_CipherMode_CBC, was already specified in the call to - * OEMCrypto_SelectKey. The encryption pattern is specified by the fields in - * the parameter pattern. A description of partial encryption patterns for - * 'cbcs' can be found in the ISO-CENC standard, section 10.4. + * 'cenc' SCHEME: * - * 'cenc' SCHEME: + * The 'cenc' scheme is OEMCrypto_CipherMode_CTR without an encryption + * pattern. All the bytes in the encrypted portion of each subsample are + * encrypted. In the pattern parameter, both the encrypt and skip fields will + * be zero. * - * The 'cenc' scheme is OEMCrypto_CipherMode_CTR without an encryption - * pattern. All the bytes in the encrypted portion of each subsample are - * encrypted. In the pattern parameter, both the encrypt and skip fields will - * be zero. + * The length of a crypto block in AES-128 is 16 bytes. In the 'cenc' scheme, + * if an encrypted subsample has a length that is not a multiple of 16 bytes, + * then all the bytes of the encrypted subsample must be decrypted, but the + * next encrypted subsample will begin by completing the incomplete crypto + * block from the previous encrypted subsample. The following diagram + * provides an example: * - * The length of a crypto block in AES-128 is 16 bytes. In the 'cenc' scheme, - * if an encrypted subsample has a length that is not a multiple of 16 bytes, - * then all the bytes of the encrypted subsample must be decrypted, but the - * next encrypted subsample will begin by completing the incomplete crypto - * block from the previous encrypted subsample. The following diagram - * provides an example: + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * To help with this, the block_offset field of each subsample will contain + * the number of bytes the initial crypto block of that subsample should be + * offset by. In the example above, the block_offset for the first subsample + * would be 0 and the block_offset for the second subsample would be 12. + * 'cenc' is the only mode that allows for a nonzero block_offset. This field + * satisfies 0 <= block_offset < 16. * - * To help with this, the block_offset field of each subsample will contain - * the number of bytes the initial crypto block of that subsample should be - * offset by. In the example above, the block_offset for the first subsample - * would be 0 and the block_offset for the second subsample would be 12. - * 'cenc' is the only mode that allows for a nonzero block_offset. This field - * satisfies 0 <= block_offset < 16. + * 'cbcs' SCHEME: * - * 'cbcs' SCHEME: + * The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern. + * Only some of the bytes in the encrypted portion of each subsample are + * encrypted. In the pattern parameter, the encrypt and skip fields will + * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content, + * SAMPLE-AES HLS content, as well as content using the DASH 'cbcs' scheme. * - * The 'cbcs' scheme is OEMCrypto_CipherMode_CBC with an encryption pattern. - * Only some of the bytes in the encrypted portion of each subsample are - * encrypted. In the pattern parameter, the encrypt and skip fields will - * usually be non-zero. This mode allows devices to decrypt FMP4 HLS content, - * SAMPLE-AES HLS content, as well as content using the DASH 'cbcs' scheme. + * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If + * the skip field is zero, then patterns are not in use and all crypto blocks + * in the encrypted part of the subsample are encrypted. It is not valid for + * the encrypt field to be zero. * - * The skip field of OEMCrypto_CENCEncryptPatternDesc may also be zero. If - * the skip field is zero, then patterns are not in use and all crypto blocks - * in the encrypted part of the subsample are encrypted. It is not valid for - * the encrypt field to be zero. + * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, + * if the encrypted part of a subsample has a length that is not a multiple + * of 16 bytes, then the final bytes that do not make up a full crypto block + * are clear and should never be decrypted. The following diagram provides an + * example: * - * The length of a crypto block in AES-128 is 16 bytes. In the 'cbcs' scheme, - * if the encrypted part of a subsample has a length that is not a multiple - * of 16 bytes, then the final bytes that do not make up a full crypto block - * are clear and should never be decrypted. The following diagram provides an - * example: + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * Whether any given protected block is actually encrypted also depends on + * the pattern. But the bytes at the end that do not make up a full crypto + * block will never be encrypted, regardless of what the pattern is. Even if + * the pattern says to decrypt every protected block, these bytes are clear + * and should not be decrypted. * - * Whether any given protected block is actually encrypted also depends on - * the pattern. But the bytes at the end that do not make up a full crypto - * block will never be encrypted, regardless of what the pattern is. Even if - * the pattern says to decrypt every protected block, these bytes are clear - * and should not be decrypted. + * Of course, if the encrypted subsample has a length that is a multiple of + * 16 bytes, all the bytes in it are protected, and they may need to be + * decrypted following the pattern. The following diagram provides an example: * - * Of course, if the encrypted subsample has a length that is a multiple of - * 16 bytes, all the bytes in it are protected, and they may need to be - * decrypted following the pattern. The following diagram provides an example: + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * INITIALIZATION VECTOR BETWEEN SUBSAMPLES: * - * INITIALIZATION VECTOR BETWEEN SUBSAMPLES: + * The IV is specified for the initial subsample in a sample in the iv field + * of the OEMCrypto_SampleDescription. OEMCrypto is responsible for correctly + * updating the IV for subsequent subsamples according to the ISO Common + * Encryption standard (ISO/IEC 23001-7:2016). Section 9.5.2.3 covers 'cenc' + * and section 9.5.2.5 covers 'cbcs'. A summary of the ISO-CENC behavior + * follows: * - * The IV is specified for the initial subsample in a sample in the iv field - * of the OEMCrypto_SampleDescription. OEMCrypto is responsible for correctly - * updating the IV for subsequent subsamples according to the ISO Common - * Encryption standard (ISO/IEC 23001-7:2016). Section 9.5.2.3 covers 'cenc' - * and section 9.5.2.5 covers 'cbcs'. A summary of the ISO-CENC behavior - * follows: + * For 'cenc', the IV at the end of each subsample carries forward to the + * next subsample and becomes the IV at the beginning of the next subsample. + * If the subsample ends on a crypto block boundary, then the IV should be + * incremented as normal at the end of the crypto block. If the subsample + * ends in the middle of a crypto block, the same IV should continue to be + * used until the crypto block is completed in the next subsample. Only + * increment the IV after the partial crypto block is completed. * - * For 'cenc', the IV at the end of each subsample carries forward to the - * next subsample and becomes the IV at the beginning of the next subsample. - * If the subsample ends on a crypto block boundary, then the IV should be - * incremented as normal at the end of the crypto block. If the subsample - * ends in the middle of a crypto block, the same IV should continue to be - * used until the crypto block is completed in the next subsample. Only - * increment the IV after the partial crypto block is completed. + * For 'cbcs', the IV is reset at the beginning of each subsample. Each + * subsample should start with the IV that was passed into + * OEMCrypto_DecryptCENC. * - * For 'cbcs', the IV is reset at the beginning of each subsample. Each - * subsample should start with the IV that was passed into - * OEMCrypto_DecryptCENC. + * To phrase it another way: In 'cenc', the encrypted portions of the + * subsamples can be concatenated to form one continuous ciphertext. In + * 'cbcs', each encrypted portion of a subsample is a separate ciphertext. + * Each separate ciphertext begins with the IV specified in the iv field of + * the OEMCrypto_SampleDescription. * - * To phrase it another way: In 'cenc', the encrypted portions of the - * subsamples can be concatenated to form one continuous ciphertext. In - * 'cbcs', each encrypted portion of a subsample is a separate ciphertext. - * Each separate ciphertext begins with the IV specified in the iv field of - * the OEMCrypto_SampleDescription. + * INITIALIZATION VECTOR WITHIN SUBSAMPLES: * - * INITIALIZATION VECTOR WITHIN SUBSAMPLES: + * Once it has the IV for each subsample, OEMCrypto is responsible for + * correctly updating the IV for each crypto block of each encrypted + * subsample portion, as outlined in the ISO Common Encryption standard + * (ISO/IEC 23001-7:2016). Section 9.5.1 includes general information about + * IVs in subsample decryption. A summary of the ISO-CENC behavior follows: * - * Once it has the IV for each subsample, OEMCrypto is responsible for - * correctly updating the IV for each crypto block of each encrypted - * subsample portion, as outlined in the ISO Common Encryption standard - * (ISO/IEC 23001-7:2016). Section 9.5.1 includes general information about - * IVs in subsample decryption. A summary of the ISO-CENC behavior follows: + * For 'cenc', the subsample's IV is the counter value to be used for the + * initial encrypted block of the subsample. The IV length is the AES block + * size. For subsequent encrypted AES blocks, OEMCrypto must calculate the IV + * by incrementing the lower 64 bits (byte 8-15) of the IV value used for the + * previous block. The counter rolls over to zero when it reaches its maximum + * value (0xFFFFFFFFFFFFFFFF). The upper 64 bits (byte 0-7) of the IV do not + * change. * - * For 'cenc', the subsample's IV is the counter value to be used for the - * initial encrypted block of the subsample. The IV length is the AES block - * size. For subsequent encrypted AES blocks, OEMCrypto must calculate the IV - * by incrementing the lower 64 bits (byte 8-15) of the IV value used for the - * previous block. The counter rolls over to zero when it reaches its maximum - * value (0xFFFFFFFFFFFFFFFF). The upper 64 bits (byte 0-7) of the IV do not - * change. + * For 'cbcs', the subsample's IV is the initialization vector for the + * initial encrypted block of the subsample. Within each subsample, each + * crypto block is used as the IV for the next crypto block, as prescribed by + * AES-CBC. * - * For 'cbcs', the subsample's IV is the initialization vector for the - * initial encrypted block of the subsample. Within each subsample, each - * crypto block is used as the IV for the next crypto block, as prescribed by - * AES-CBC. + * NOTES: * - * NOTES: + * If the destination buffer is secure, an offset may be specified. + * OEMCrypto_DecryptCENC begins storing data buffers.output.secure.offset + * bytes after the beginning of the secure buffer. * - * If the destination buffer is secure, an offset may be specified. - * OEMCrypto_DecryptCENC begins storing data buffers.output.secure.offset - * bytes after the beginning of the secure buffer. + * If the session has an entry in the Usage Table, then OEMCrypto must update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto must update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. + * OEMCrypto cannot assume that the buffers of consecutive samples are + * consecutive in memory. * - * OEMCrypto cannot assume that the buffers of consecutive samples are - * consecutive in memory. + * A subsample may consist entirely of encrypted bytes or clear bytes. In + * this case, the clear or the encrypted part of the subsample will be zero, + * indicating that no bytes of that kind appear in the subsample. * - * A subsample may consist entirely of encrypted bytes or clear bytes. In - * this case, the clear or the encrypted part of the subsample will be zero, - * indicating that no bytes of that kind appear in the subsample. + * The ISO-CENC spec implicitly limits both the skip and encrypt values to be + * 4 bits, so they are at most 15. * - * The ISO-CENC spec implicitly limits both the skip and encrypt values to be - * 4 bits, so they are at most 15. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * If OEMCrypto assembles all of the encrypted subsample portions into a + * single buffer and then decrypts it in one pass, it can assume that the + * block offset is 0. * - * If OEMCrypto assembles all of the encrypted subsample portions into a - * single buffer and then decrypts it in one pass, it can assume that the - * block offset is 0. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") - * - * Verification: - * The total size of all the subsamples cannot exceed the total size of the - * input buffer. OEMCrypto integrations should validate this and return - * OEMCrypto_ERROR_UNKNOWN_FAILURE if the subsamples are larger than the - * input buffer. No decryption should be performed in this case. - * If the subsamples all contain only clear bytes, then no further - * verification is performed. This call shall copy clear data even when there - * are no keys loaded, or there is no selected key. - * If this is the first use of a key for this session, then OEMCrypto shall - * call ODK_AttemptFirstPlayback to update the session's clock values and - * verify playback is allowed. If this is not the first use of a key for this - * session, then OEMCrypto shall call ODK_UpdateLastPlaybackTime. See the - * document "License Duration and Renewal" for handling the return value of - * these ODK functions. - * The following checks should be performed if any subsamples contain any - * encrypted bytes. If any check fails, an error is returned, and no - * decryption is performed. - * 1. If the current key's control block has the Data_Path_Type bit set, - * then the API shall verify that the output buffer is secure or direct. - * If not, return OEMCrypto_ERROR_DECRYPT_FAILED. - * 2. If the current key control block has the bit Disable_Analog_Output - * set, then the device should disable analog video output. If the - * device has analog video output that cannot be disabled, then - * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. (See note on delayed - * error conditions below) - * 3. If the current key's control block has the HDCP bit set, then the API - * shall verify that the buffer will be displayed locally, or output - * externally using HDCP only. If not, return - * OEMCrypto_ERROR_INSUFFICIENT_HDCP. (See note on delayed error - * conditions below) - * 4. If the current key's control block has a nonzero value for - * HDCP_Version, then the current version of HDCP for the device and the - * display combined will be compared against the version specified in - * the control block. If the current version is not at least as high as - * that in the control block, and the device is not able to restrict - * displays with HDCP levels lower than what's in the control block, - * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. If the device is able to - * restrict those displays, return - * OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. (See note on delayed - * error conditions below) - * 5. If the current session has an entry in the Usage Table, and the - * status of that entry is either kInactiveUsed or kInactiveUnused, then - * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. - * 6. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash, - * and the current key's control block does not have the - * Allow_Hash_Verification bit set, then do not compute a hash and - * return OEMCrypto_ERROR_UNKNOWN_FAILURE. + * @verification + * The total size of all the subsamples cannot exceed the total size of the + * input buffer. OEMCrypto integrations should validate this and return + * OEMCrypto_ERROR_UNKNOWN_FAILURE if the subsamples are larger than the + * input buffer. No decryption should be performed in this case. + * If the subsamples all contain only clear bytes, then no further + * verification is performed. This call shall copy clear data even when there + * are no keys loaded, or there is no selected key. + * If this is the first use of a key for this session, then OEMCrypto shall + * call ODK_AttemptFirstPlayback to update the session's clock values and + * verify playback is allowed. If this is not the first use of a key for this + * session, then OEMCrypto shall call ODK_UpdateLastPlaybackTime. See the + * document "License Duration and Renewal" for handling the return value of + * these ODK functions. + * The following checks should be performed if any subsamples contain any + * encrypted bytes. If any check fails, an error is returned, and no + * decryption is performed. + * 1. If the current key's control block has the Data_Path_Type bit set, + * then the API shall verify that the output buffer is secure or direct. + * If not, return OEMCrypto_ERROR_DECRYPT_FAILED. + * 2. If the current key control block has the bit Disable_Analog_Output + * set, then the device should disable analog video output. If the + * device has analog video output that cannot be disabled, then + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. (See note on delayed + * error conditions below) + * 3. If the current key's control block has the HDCP bit set, then the API + * shall verify that the buffer will be displayed locally, or output + * externally using HDCP only. If not, return + * OEMCrypto_ERROR_INSUFFICIENT_HDCP. (See note on delayed error + * conditions below) + * 4. If the current key's control block has a nonzero value for + * HDCP_Version, then the current version of HDCP for the device and the + * display combined will be compared against the version specified in + * the control block. If the current version is not at least as high as + * that in the control block, and the device is not able to restrict + * displays with HDCP levels lower than what's in the control block, + * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. If the device is able to + * restrict those displays, return + * OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. (See note on delayed + * error conditions below) + * 5. If the current session has an entry in the Usage Table, and the + * status of that entry is either kInactiveUsed or kInactiveUnused, then + * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. + * 6. If a Decrypt Hash has been initialized via OEMCrypto_SetDecryptHash, + * and the current key's control block does not have the + * Allow_Hash_Verification bit set, then do not compute a hash and + * return OEMCrypto_ERROR_UNKNOWN_FAILURE. * * Delayed Error Conditions * - * On some devices, the HDCP subsystem is not directly connected to the - * OEMCrypto TA. This means that returning the error - * OEMCrypto_ERROR_INSUFFICIENT_HDCP at the time of the decrypt call is a - * performance hit. However, some devices have the ability to tag output - * buffers with security requirements, such as the required HDCP level. - * For those devices, when a call to OEMCrypto_DecryptCENC is made using a - * key that requires HDCP output, and if the HDCP level on the output does - * not meet the required level. - * - OEMCrypto may tag the output buffer as requiring HDCP at the required - * level and return OEMCrypto_SUCCESS. - * - Output shall not be sent to the display. - * - On the second or third call to OEMCrypto_DecryptCENC with the same - * key, OEMCrypto shall return OEMCrypto_ERROR_INSUFFICIENT_HDCP. - * For those devices, when a call to OEMCrypto_DecryptCENC is made using a - * key that requires HDCP output, and if the HDCP level on some of the - * displays does not meet the required level. - * - OEMCrypto may tag the output buffer as requiring HDCP at the required - * level and return OEMCrypto_SUCCESS. - * - Output shall only be sent to the display with sufficient output - * control, e.g. the local display. - * - On the second or third call to OEMCrypto_DecryptCENC with the same - * key, OEMCrypto shall return OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. - * In either case, a call to OEMCrypto_GetHDCPCapability shall return the - * current HDCP level. + * On some devices, the HDCP subsystem is not directly connected to the + * OEMCrypto TA. This means that returning the error + * OEMCrypto_ERROR_INSUFFICIENT_HDCP at the time of the decrypt call is a + * performance hit. However, some devices have the ability to tag output + * buffers with security requirements, such as the required HDCP level. + * For those devices, when a call to OEMCrypto_DecryptCENC is made using a + * key that requires HDCP output, and if the HDCP level on the output does + * not meet the required level. + * - OEMCrypto may tag the output buffer as requiring HDCP at the required + * level and return OEMCrypto_SUCCESS. + * - Output shall not be sent to the display. + * - On the second or third call to OEMCrypto_DecryptCENC with the same + * key, OEMCrypto shall return OEMCrypto_ERROR_INSUFFICIENT_HDCP. + * For those devices, when a call to OEMCrypto_DecryptCENC is made using a + * key that requires HDCP output, and if the HDCP level on some of the + * displays does not meet the required level. + * - OEMCrypto may tag the output buffer as requiring HDCP at the required + * level and return OEMCrypto_SUCCESS. + * - Output shall only be sent to the display with sufficient output + * control, e.g. the local display. + * - On the second or third call to OEMCrypto_DecryptCENC with the same + * key, OEMCrypto shall return OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION. + * In either case, a call to OEMCrypto_GetHDCPCapability shall return the + * current HDCP level. * - * Parameters: - * [in] session: Crypto session identifier. The crypto session in which - * decrypt is to be performed. - * [in] samples: A caller-owned array of OEMCrypto_SampleDescription - * structures. Each entry in this array contains one sample of the - * content. - * [in] samples_length: The length of the array pointed to by the samples - * parameter. - * [in] pattern: A caller-owned structure indicating the encrypt/skip pattern - * as specified in the ISO-CENC standard. + * @param[in] session: Crypto session identifier. The crypto session in which + * decrypt is to be performed. + * @param[in] samples: A caller-owned array of OEMCrypto_SampleDescription + * structures. Each entry in this array contains one sample of the content. + * @param[in] samples_length: The length of the array pointed to by the samples + * parameter. + * @param[in] pattern: A caller-owned structure indicating the encrypt/skip + * pattern as specified in the ISO-CENC standard. * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_DECRYPT_FAILED - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_INSUFFICIENT_HDCP - * OEMCrypto_ERROR_ANALOG_OUTPUT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_OUTPUT_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_DECRYPT_FAILED + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_INSUFFICIENT_HDCP + * @retval OEMCrypto_ERROR_ANALOG_OUTPUT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support subsample sizes and total input buffer sizes as * specified by its resource rating tier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is @@ -2411,14 +2448,14 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * application, which can decide to skip the current frame of video or to * switch to a lower resolution. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. This method changed its name in API * version 11. */ @@ -2428,65 +2465,57 @@ OEMCryptoResult OEMCrypto_DecryptCENC( size_t samples_length, // the number of samples. const OEMCrypto_CENCEncryptPatternDesc* pattern); -/* - * OEMCrypto_CopyBuffer +/** + * Copies the payload in the buffer referenced by the *data parameter into + * the buffer referenced by the out_buffer parameter. The data is simply + * copied. The definition of OEMCrypto_DestBufferDesc and subsample_flags are + * the same as in OEMCrypto_DecryptCENC, above. * - * Description: - * Copies the payload in the buffer referenced by the *data parameter into - * the buffer referenced by the out_buffer parameter. The data is simply - * copied. The definition of OEMCrypto_DestBufferDesc and subsample_flags are - * the same as in OEMCrypto_DecryptCENC, above. + * The main difference between this and DecryptCENC is that this function may be + * used before a license is loaded into a session. In particular, an application + * will use this to copy the clear leader of a video to a secure buffer while + * the license request is being generated, sent to the server, and the response + * is being processed. This functionality is needed because an application may + * not have read or write access to a secure destination buffer. * - * The main difference between this and DecryptCENC is that this function - * does not need an open session, and it may be called concurrently with - * other functions on a multithreaded system. In particular, an application - * will use this to copy the clear leader of a video to a secure buffer while - * the license request is being generated, sent to the server, and the - * response is being processed. This functionality is needed because an - * application may not have read or write access to a secure destination - * buffer. + * NOTES: * - * NOTES: + * This method may be called several times before the data is used. The first + * buffer in a chunk of data will have the OEMCrypto_FirstSubsample bit set + * in subsample_flags. The last buffer in a chunk of data will have the + * OEMCrypto_LastSubsample bit set in subsample_flags. The data will not be + * used until after OEMCrypto_LastSubsample has been set. If an + * implementation copies data immediately, it may ignore subsample_flags. * - * This method may be called several times before the data is used. The first - * buffer in a chunk of data will have the OEMCrypto_FirstSubsample bit set - * in subsample_flags. The last buffer in a chunk of data will have the - * OEMCrypto_LastSubsample bit set in subsample_flags. The data will not be - * used until after OEMCrypto_LastSubsample has been set. If an - * implementation copies data immediately, it may ignore subsample_flags. + * If the destination buffer is secure, an offset may be specified. + * CopyBuffer begins storing data out_buffer->secure.offset bytes after the + * beginning of the secure buffer. * - * If the destination buffer is secure, an offset may be specified. - * CopyBuffer begins storing data out_buffer->secure.offset bytes after the - * beginning of the secure buffer. + * @verification + * The following checks should be performed. + * 1. If either data or out_buffer is null, return + * OEMCrypto_ERROR_INVALID_CONTEXT. * - * Verification: - * The following checks should be performed. - * 1. If either data or out_buffer is null, return - * OEMCrypto_ERROR_INVALID_CONTEXT. + * @param[in] session: crypto session identifier. + * @param[in] data_addr: An unaligned pointer to the buffer to be copied. + * @param[in] data_addr_length: The length of the buffer, in bytes. + * @param[in] out_buffer_descriptor: A caller-owned descriptor that specifies + * the handling of the byte stream. See OEMCrypto_DestbufferDesc for details. + * @param[in] subsample_flags: bitwise flags indicating if this is the first, + * middle, or last subsample in a chunk of data. 1 = first subsample, 2 = + * last subsample, 3 = both first and last subsample, 0 = neither first nor + * last subsample. * - * Parameters: - * [in] session: crypto session identifier. - * [in] data_addr: An unaligned pointer to the buffer to be copied. - * [in] data_addr_length: The length of the buffer, in bytes. - * [in] out_buffer_descriptor: A caller-owned descriptor that specifies the - * handling of the byte stream. See OEMCrypto_DestbufferDesc for - * details. - * [in] subsample_flags: bitwise flags indicating if this is the first, - * middle, or last subsample in a chunk of data. 1 = first subsample, 2 - * = last subsample, 3 = both first and last subsample, 0 = neither - * first nor last subsample. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_OUTPUT_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support subsample sizes and total input buffer sizes as * specified by its resource rating tier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is @@ -2501,14 +2530,14 @@ OEMCryptoResult OEMCrypto_DecryptCENC( * application, which can decide to skip the current frame of video or to * switch to a lower resolution. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is changed in API version 15. */ OEMCryptoResult OEMCrypto_CopyBuffer( @@ -2517,19 +2546,16 @@ OEMCryptoResult OEMCrypto_CopyBuffer( const OEMCrypto_DestBufferDesc* out_buffer_descriptor, uint8_t subsample_flags); -/* - * OEMCrypto_Generic_Encrypt +/** + * This function encrypts a generic buffer of data using the current key. * - * Description: - * This function encrypts a generic buffer of data using the current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. + * OEMCrypto shall be able to handle buffers at least 100 KiB long. * - * OEMCrypto shall be able to handle buffers at least 100 KiB long. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the data is not encrypted. * 1. The control bit for the current key shall have the Allow_Encrypt set. @@ -2544,43 +2570,41 @@ OEMCryptoResult OEMCrypto_CopyBuffer( * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] in_buffer_length: length of the buffer, in bytes. The algorithm may - * restrict in_buffer_length to be a multiple of block size. - * [in] iv: IV for encrypting data. Size is 128 bits. - * [in] algorithm: Specifies which encryption algorithm to use. Currently, - * only CBC 128 mode is allowed for encryption. - * [out] out_buffer: pointer to buffer in which encrypted data should be - * stored. + * @param[in] session: crypto session identifier. + * @param[in] in_buffer: pointer to memory containing data to be encrypted. + * @param[in] in_buffer_length: length of the buffer, in bytes. The algorithm + * may restrict in_buffer_length to be a multiple of block size. + * @param[in] iv: IV for encrypting data. Size is 128 bits. + * @param[in] algorithm: Specifies which encryption algorithm to use. + * Currently, only CBC 128 mode is allowed for encryption. + * @param[out] out_buffer: pointer to buffer in which encrypted data should be + * stored. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Encrypt( @@ -2588,19 +2612,16 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* out_buffer); -/* - * OEMCrypto_Generic_Decrypt +/** + * This function decrypts a generic buffer of data using the current key. * - * Description: - * This function decrypts a generic buffer of data using the current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. + * OEMCrypto should be able to handle buffers at least 100 KiB long. * - * OEMCrypto should be able to handle buffers at least 100 KiB long. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the data is not decrypted. * 1. The control bit for the current key shall have the Allow_Decrypt set. @@ -2617,44 +2638,42 @@ OEMCryptoResult OEMCrypto_Generic_Encrypt( * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] in_buffer: pointer to memory containing data to be encrypted. - * [in] in_buffer_length: length of the buffer, in bytes. The algorithm may - * restrict in_buffer_length to be a multiple of block size. - * [in] iv: IV for encrypting data. Size is 128 bits. - * [in] algorithm: Specifies which encryption algorithm to use. Currently, - * only CBC 128 mode is allowed for decryption. - * [out] out_buffer: pointer to buffer in which decrypted data should be - * stored. + * @param[in] session: crypto session identifier. + * @param[in] in_buffer: pointer to memory containing data to be encrypted. + * @param[in] in_buffer_length: length of the buffer, in bytes. The algorithm + * may restrict in_buffer_length to be a multiple of block size. + * @param[in] iv: IV for encrypting data. Size is 128 bits. + * @param[in] algorithm: Specifies which encryption algorithm to use. + * Currently, only CBC 128 mode is allowed for decryption. + * @param[out] out_buffer: pointer to buffer in which decrypted data should be + * stored. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_DECRYPT_FAILED - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_DECRYPT_FAILED + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Decrypt( @@ -2662,17 +2681,14 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* out_buffer); -/* - * OEMCrypto_Generic_Sign +/** + * This function signs a generic buffer of data using the current key. * - * Description: - * This function signs a generic buffer of data using the current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the data is not signed. * 1. The control bit for the current key shall have the Allow_Sign set. @@ -2686,44 +2702,43 @@ OEMCryptoResult OEMCrypto_Generic_Decrypt( * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] algorithm: Specifies which algorithm to use. - * [out] signature: pointer to buffer in which signature should be stored. - * May be null on the first call in order to find required buffer size. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature + * @param[in] session: crypto session identifier. + * @param[in] buffer: pointer to memory containing data to be encrypted. + * @param[in] buffer_length: length of the buffer, in bytes. + * @param[in] algorithm: Specifies which algorithm to use. + * @param[out] signature: pointer to buffer in which signature should be + * stored. May be null on the first call in order to find required buffer + * size. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough to - * hold the output signature. - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_SHORT_BUFFER if signature buffer is not large enough + * to hold the output signature. + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, @@ -2733,18 +2748,15 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, OEMCrypto_SharedMemory* signature, size_t* signature_length); -/* - * OEMCrypto_Generic_Verify +/** + * This function verifies the signature of a generic buffer of data using the + * current key. * - * Description: - * This function verifies the signature of a generic buffer of data using the - * current key. + * If the session has an entry in the Usage Table, then OEMCrypto will update + * the time_of_last_decrypt. If the status of the entry is "unused", then + * change the status to "active" and set the time_of_first_decrypt. * - * If the session has an entry in the Usage Table, then OEMCrypto will update - * the time_of_last_decrypt. If the status of the entry is "unused", then - * change the status to "active" and set the time_of_first_decrypt. - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned. * 1. The control bit for the current key shall have the Allow_Verify set. @@ -2764,41 +2776,39 @@ OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, * status of that entry is either kInactiveUsed or kInactiveUnused, then * return the error OEMCrypto_ERROR_LICENSE_INACTIVE. * - * Parameters: - * [in] session: crypto session identifier. - * [in] buffer: pointer to memory containing data to be encrypted. - * [in] buffer_length: length of the buffer, in bytes. - * [in] algorithm: Specifies which algorithm to use. - * [in] signature: pointer to buffer in which signature resides. - * [in] signature_length: length of the signature buffer, in bytes. + * @param[in] session: crypto session identifier. + * @param[in] buffer: pointer to memory containing data to be encrypted. + * @param[in] buffer_length: length of the buffer, in bytes. + * @param[in] algorithm: Specifies which algorithm to use. + * @param[in] signature: pointer to buffer in which signature resides. + * @param[in] signature_length: length of the signature buffer, in bytes. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_KEY_EXPIRED - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_KEY_EXPIRED + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support buffers sizes of at least 100 KiB for generic * crypto operations. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_Generic_Verify( @@ -2806,62 +2816,62 @@ OEMCryptoResult OEMCrypto_Generic_Verify( size_t buffer_length, OEMCrypto_Algorithm algorithm, const OEMCrypto_SharedMemory* signature, size_t signature_length); -/* - * OEMCrypto_WrapKeyboxOrOEMCert +/// @} + +/// @addtogroup factory_provision +/// @{ + +/** + * A device should be provisioned at the factory with either an OEM + * Certificate or a keybox. We will call this data the root of trust. During + * manufacturing, the root of trust should be encrypted with the OEM root key + * and stored on the file system in a region that will not be erased during + * factory reset. This function may be used by legacy systems that use the + * two-step WrapKeyboxOrOEMCert/InstallKeyboxOrOEMCert approach. When the + * Widevine DRM plugin initializes, it will look for a wrapped root of trust + * in the file /factory/wv.keys and install it into the security processor by + * calling OEMCrypto_InstallKeyboxOrOEMCert(). * - * Description: - * A device should be provisioned at the factory with either an OEM - * Certificate or a keybox. We will call this data the root of trust. During - * manufacturing, the root of trust should be encrypted with the OEM root key - * and stored on the file system in a region that will not be erased during - * factory reset. This function may be used by legacy systems that use the - * two-step WrapKeyboxOrOEMCert/InstallKeyboxOrOEMCert approach. When the - * Widevine DRM plugin initializes, it will look for a wrapped root of trust - * in the file /factory/wv.keys and install it into the security processor by - * calling OEMCrypto_InstallKeyboxOrOEMCert(). + * Figure 10. OEMCrypto_WrapKeyboxOrOEMCert Operation * - * Figure 10. OEMCrypto_WrapKeyboxOrOEMCert Operation + * OEMCrypto_WrapKeyboxOrOEMCert() is used to generate an OEM-encrypted root + * of trust that may be passed to OEMCrypto_InstallKeyboxOrOEMCert() for + * provisioning. The root of trust may be either passed in the clear or + * previously encrypted with a transport key. If a transport key is supplied, + * the keybox is first decrypted with the transport key before being wrapped + * with the OEM root key. This function is only needed if the root of trust + * provisioning method involves saving the keybox or OEM Certificate to the + * file system. * - * OEMCrypto_WrapKeyboxOrOEMCert() is used to generate an OEM-encrypted root - * of trust that may be passed to OEMCrypto_InstallKeyboxOrOEMCert() for - * provisioning. The root of trust may be either passed in the clear or - * previously encrypted with a transport key. If a transport key is supplied, - * the keybox is first decrypted with the transport key before being wrapped - * with the OEM root key. This function is only needed if the root of trust - * provisioning method involves saving the keybox or OEM Certificate to the - * file system. + * @param[in] keybox_or_cert: pointer to root of trust data to encrypt -- this + * is either a keybox or an OEM Certificate private key. May be NULL on the + * first call to test the size of the wrapped keybox. The keybox may either + * be clear or previously encrypted. + * @param[in] keybox_or_cert_length: length the keybox or cert data in bytes + * @param[out] wrapped_keybox_or_cert: Pointer to wrapped keybox or cert + * @param[in,out] wrapped_keybox_or_cert_length: Pointer to the length of the + * wrapped keybox or certificate key in bytes + * @param[in] transport_key: Optional. AES transport key. If provided, the + * keybox_or_cert parameter was previously encrypted with this key. The + * keybox will be decrypted with the transport key using AES-CBC and a null + * IV. + * @param[in] transport_key_length: Optional. Number of bytes in the + * transport_key, if used. * - * Parameters: - * [in] keybox_or_cert - pointer to root of trust data to encrypt -- this is - * either a keybox or an OEM Certificate private key. May be NULL on - * the first call to test size of wrapped keybox. The keybox may either - * be clear or previously encrypted. - * [in] keybox_or_cert_length - length the keybox or cert data in bytes - * [out] wrapped_keybox_or_cert – Pointer to wrapped keybox or cert - * [out] wrapped_keybox_or_cert_length – Pointer to the length of the wrapped - * keybox or certificate key in bytes - * [in] transport_key – Optional. AES transport key. If provided, the - * keybox_or_cert parameter was previously encrypted with this key. The - * keybox will be decrypted with the transport key using AES-CBC and a - * null IV. - * [in] transport_key_length – Optional. Number of bytes in the - * transport_key, if used. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_WRITE_KEYBOX failed to encrypt the keybox + * @retval OEMCrypto_ERROR_SHORT_BUFFER if keybox is provided as NULL, to + * determine the size of the wrapped keybox + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_WRITE_KEYBOX failed to encrypt the keybox - * OEMCrypto_ERROR_SHORT_BUFFER if keybox is provided as NULL, to determine - * the size of the wrapped keybox - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( @@ -2869,714 +2879,606 @@ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( uint8_t* wrapped_keybox_or_cert, size_t* wrapped_keybox_or_cert_length, const uint8_t* transport_key, size_t transport_key_length); -/* - * OEMCrypto_InstallKeyboxOrOEMCert +/** + * Decrypts a wrapped root of trust and installs it in the security + * processor. The root of trust is unwrapped then encrypted with the OEM root + * key. This function is called from the Widevine DRM plugin at + * initialization time if there is no valid root of trust installed. It looks + * for wrapped data in the file /factory/wv.keys and if it is present, will + * read the file and call OEMCrypto_InstallKeyboxOrOEMCert() with the + * contents of the file. This function is only needed if the factory + * provisioning method involves saving the keybox or OEM Certificate to the + * file system. * - * Description: - * Decrypts a wrapped root of trust and installs it in the security - * processor. The root of trust is unwrapped then encrypted with the OEM root - * key. This function is called from the Widevine DRM plugin at - * initialization time if there is no valid root of trust installed. It looks - * for wrapped data in the file /factory/wv.keys and if it is present, will - * read the file and call OEMCrypto_InstallKeyboxOrOEMCert() with the - * contents of the file. This function is only needed if the factory - * provisioning method involves saving the keybox or OEM Certificate to the - * file system. + * @param[in] keybox_or_cert: pointer to encrypted data as input + * @param[in] keybox_or_cert_length: length of the data in bytes * - * Parameters: - * [in] keybox_or_cert - pointer to encrypted data as input - * [in] keybox_or_cert_length - length of the data in bytes + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_BAD_MAGIC + * @retval OEMCrypto_ERROR_BAD_CRC + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_BAD_MAGIC - * OEMCrypto_ERROR_BAD_CRC - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox_or_cert, size_t keybox_or_cert_length); -/* - * OEMCrypto_GetProvisioningMethod +/** + * This function is for OEMCrypto to tell the layer above what provisioning + * method it uses: keybox or OEM certificate. * - * Description: - * This function is for OEMCrypto to tell the layer above what provisioning - * method it uses: keybox or OEM certificate. + * @retval OEMCrypto_DrmCertificate means the device has a DRM certificate built + * into the system. This cannot be used by level 1 devices. This + * provisioning method is deprecated and should not be used on new + * devices. OEMCertificate provisioning should be used instead. + * @retval OEMCrypto_Keybox means the device has a unique keybox. For level 1 + * devices this keybox must be securely installed by the device + * manufacturer. + * @retval OEMCrypto_OEMCertificate means the device has a factory installed OEM + * certificate. This is also called Provisioning 3.0. + * @retval OEMCrypto_ProvisioningError indicates a serious problem with the + * OEMCrypto library. * - * Parameters: - * none - * - * Returns: - * - DrmCertificate means the device has a DRM certificate built into the - * system. This cannot be used by level 1 devices. This provisioning - * method is deprecated and should not be used on new devices. - * OEMCertificate provisioning should be used instead. - * - Keybox means the device has a unique keybox. For level 1 devices this - * keybox must be securely installed by the device manufacturer. - * - OEMCertificate means the device has a factory installed OEM - * certificate. This is also called Provisioning 3.0. - * - ProvisioningError indicates a serious problem with the OEMCrypto - * library. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new API version 12. */ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(void); -/* - * OEMCrypto_IsKeyboxOrOEMCertValid +/** + * If the device has a keybox, this validates the Widevine Keybox loaded into + * the security processor device. This method verifies two fields in the + * keybox: * - * Description: - * If the device has a keybox, this validates the Widevine Keybox loaded into - * the security processor device. This method verifies two fields in the - * keybox: + * - Verify the MAGIC field contains a valid signature (such as, + * 'k''b''o''x'). + * - Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the + * checksum to the CRC stored in the Keybox. + * The CRC is computed over the entire Keybox excluding the 4 bytes of the + * CRC (for example, Keybox[0..123]). For a description of the fields stored + * in the keybox, see Keybox Definition. * - * - Verify the MAGIC field contains a valid signature (such as, - * 'k''b''o''x'). - * - Compute the CRC using CRC-32-POSIX-1003.2 standard and compare the - * checksum to the CRC stored in the Keybox. - * The CRC is computed over the entire Keybox excluding the 4 bytes of the - * CRC (for example, Keybox[0..123]). For a description of the fields stored - * in the keybox, see Keybox Definition. + * If the device has an OEM Certificate, this validates the certificate + * private key. * - * If the device has an OEM Certificate, this validates the certificate - * private key. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_BAD_MAGIC + * @retval OEMCrypto_ERROR_BAD_CRC + * @retval OEMCrypto_ERROR_KEYBOX_INVALID + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_BAD_MAGIC - * OEMCrypto_ERROR_BAD_CRC - * OEMCrypto_ERROR_KEYBOX_INVALID - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void); -/* - * OEMCrypto_GetDeviceID +/** + * Return a device unique id. For devices with a keybox, retrieve the + * DeviceID from the Keybox. For devices that have an OEM Certificate instead + * of a keybox, it should set the device ID to a device-unique string, such + * as the device serial number. The ID should be device-unique and it should + * be stable -- i.e. it should not change across a device reboot or a system + * upgrade. This shall match the device id found in the core provisioning + * request message. The maximum length of the device id is 64 bytes. The + * device ID field in a keybox is 32 bytes. * - * Description: - * Return a device unique id. For devices with a keybox, retrieve the - * DeviceID from the Keybox. For devices that have an OEM Certificate instead - * of a keybox, it should set the device ID to a device-unique string, such - * as the device serial number. The ID should be device-unique and it should - * be stable -- i.e. it should not change across a device reboot or a system - * upgrade. This shall match the device id found in the core provisioning - * request message. The maximum length of the device id is 64 bytes. The - * device ID field in a keybox is 32 bytes. + * @param[out] device_id: pointer to the buffer that receives the Device ID. + * @param[in,out] device_id_length - on input, size of the caller's device ID + * buffer. On output, the number of bytes written into the buffer. * - * Parameters: - * [out] device_id - pointer to the buffer that receives the Device ID. - * [in/out] device_id_length – on input, size of the caller's device ID - * buffer. On output, the number of bytes written into the buffer. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return + * device ID + * @retval OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return device ID - * OEMCrypto_ERROR_NO_DEVICEID failed to return Device Id - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetDeviceID(uint8_t* device_id, size_t* device_id_length); -/* - * OEMCrypto_GetKeyData +/// @} + +/// @addtogroup keybox +/// @{ + +/** + * Return the Key Data field from the Keybox. * - * Description: - * Return the Key Data field from the Keybox. + * @param[out] keyData: pointer to the buffer to hold the Key Data field from + * the Keybox + * @param[in,out] keyDataLength: on input, the allocated buffer size. On output, + * the number of bytes in Key Data * - * Parameters: - * [out] keyData - pointer to the buffer to hold the Key Data field from the - * Keybox - * [in/out] keyDataLength – on input, the allocated buffer size. On output, - * the number of bytes in Key Data + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return + * KeyData + * @retval OEMCrypto_ERROR_NO_KEYDATA + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED: this function is for + * Provisioning 2.0 only. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if the buffer is too small to return KeyData - * OEMCrypto_ERROR_NO_KEYDATA - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 2.0 - * only. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetKeyData(uint8_t* key_data, size_t* key_data_length); -/* - * OEMCrypto_LoadTestKeybox +/** + * Temporarily use the specified test keybox until the next call to + * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run + * on a production device without permanently changing the keybox. Using the + * test keybox is not persistent. OEMCrypto cannot assume that this keybox is + * the same as previous keyboxes used for testing. * - * Description: - * Temporarily use the specified test keybox until the next call to - * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run - * on a production device without permanently changing the keybox. Using the - * test keybox is not persistent. OEMCrypto cannot assume that this keybox is - * the same as previous keyboxes used for testing. + * Devices that use an OEM Certificate instead of a keybox (i.e. Provisioning + * 3.0) do not need to support this functionality, and may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that use an OEM Certificate instead of a keybox (i.e. Provisioning - * 3.0) do not need to support this functionality, and may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[in] buffer: pointer to memory containing test keybox, in binary form. + * @param[in] buffer_length: length of the buffer, in bytes. * - * Parameters: - * [in] buffer: pointer to memory containing test keybox, in binary form. - * [in] buffer_length: length of the buffer, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED this function is for + * Provisioning 2.0 only. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 2.0 - * only. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. It is called after OEMCrypto_Initialize and * after OEMCrypto_GetProvisioningMethod and only if the provisoining method * is OEMCrypto_Keybox, * - * Version: + * @version * This method changed in API version 14. */ OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_LoadOEMPrivateKey +/// @} + +/// @addtogroup oem_cert +/// @{ +/** + * After a call to this function, all session functions using an RSA key + * should use the OEM certificate's private RSA key. See the section above + * discussing Provisioning 3.0. * - * Description: - * After a call to this function, all session functions using an RSA key - * should use the OEM certificate's private RSA key. See the section above - * discussing Provisioning 3.0. + * @param[in] session: this function affects the specified session only. * - * Parameters: - * - [in] session: this function affects the specified session only. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED this function is for + * Provisioning 3.0 only. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 3.0 - * only. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new API version 16. */ OEMCryptoResult OEMCrypto_LoadOEMPrivateKey(OEMCrypto_SESSION session); -/* - * OEMCrypto_GetOEMPublicCertificate +/** + * This function should place the OEM public certificate in the buffer + * public_cert. See the section above discussing Provisioning 3.0. * - * Description: - * This function should place the OEM public certificate in the buffer - * public_cert. See the section above discussing Provisioning 3.0. + * If the buffer is not large enough, OEMCrypto should update + * public_cert_length and return OEMCrypto_ERROR_SHORT_BUFFER. * - * If the buffer is not large enough, OEMCrypto should update - * public_cert_length and return OEMCrypto_ERROR_SHORT_BUFFER. + * @param[out] public_cert: the buffer where the public certificate is stored. + * @param[in,out] public_cert_length: on input, this is the available size of + * the buffer. On output, this is the number of bytes needed for the + * certificate. * - * Parameters: - * - [out] public_cert: the buffer where the public certificate is stored. - * - [in/out] public_cert_length: on input, this is the available size of - * the buffer. On output, this is the number of bytes needed for the - * certificate. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED this function is for + * Provisioning 3.0 only. + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_NOT_IMPLEMENTED - this function is for Provisioning 3.0 - * only. - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new API version 16. */ OEMCryptoResult OEMCrypto_GetOEMPublicCertificate(uint8_t* public_cert, size_t* public_cert_length); -/* - * OEMCrypto_GetRandom +/// @} + +/// @addtogroup validation +/// @{ + +/** + * Returns a buffer filled with hardware-generated random bytes, if supported + * by the hardware. If the hardware feature does not exist, return + * OEMCrypto_ERROR_RNG_NOT_SUPPORTED. * - * Description: - * Returns a buffer filled with hardware-generated random bytes, if supported - * by the hardware. If the hardware feature does not exist, return - * OEMCrypto_ERROR_RNG_NOT_SUPPORTED. + * @param[out] random_data: pointer to the buffer that receives random data + * @param[in] random_data_length: length of the random data buffer in bytes * - * Parameters: - * [out] random_data- pointer to the buffer that receives random data - * [in] random_data_length- length of the random data buffer in bytes + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_RNG_FAILED failed to generate random number + * @retval OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_RNG_FAILED failed to generate random number - * OEMCrypto_ERROR_RNG_NOT_SUPPORTED function not supported - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: - * OEMCrypto shall support random_data_length- sizes of at least 32 bytes + * @buffer_size + * OEMCrypto shall support random_data_length sizes of at least 32 bytes * for random number generation. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is supported in all API versions. */ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* random_data, size_t random_data_length); -/* - * OEMCrypto_APIVersion +/** + * This function returns the current API version number. The version number + * allows the calling application to avoid version mis-match errors, because + * this API is part of a shared library. * - * Description: - * This function returns the current API version number. The version number - * allows the calling application to avoid version mis-match errors, because - * this API is part of a shared library. + * There is a possibility that some API methods will be backwards compatible, + * or backwards compatible at a reduced security level. * - * There is a possibility that some API methods will be backwards compatible, - * or backwards compatible at a reduced security level. + * There is no plan to introduce forward-compatibility. Applications will + * reject a library with a newer version of the API. * - * There is no plan to introduce forward-compatibility. Applications will - * reject a library with a newer version of the API. + * The version specified in this document is 16. Any OEM that returns this + * version number guarantees it passes all unit tests associated with this + * version. * - * The version specified in this document is 16. Any OEM that returns this - * version number guarantees it passes all unit tests associated with this - * version. + * @retval The supported API, as specified in the header file OEMCryptoCENC.h. * - * Parameters: - * none - * - * Returns: - * The supported API, as specified in the header file OEMCryptoCENC.h. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in each API version. */ uint32_t OEMCrypto_APIVersion(void); -/* - * OEMCrypto_MinorAPIVersion +/** + * This function returns the current API minor version number. The version + * number allows the calling application to avoid version mis-match errors, + * because this API is part of a shared library. * - * Description: - * This function returns the current API minor version number. The version - * number allows the calling application to avoid version mis-match errors, - * because this API is part of a shared library. + * The minor version specified in this document is 2. Any OEM that returns + * this version number guarantees it passes all unit tests associated with + * this version. * - * The minor version specified in this document is 2. Any OEM that returns - * this version number guarantees it passes all unit tests associated with - * this version. + * @retval The supported API, as specified in the header file OEMCryptoCENC.h. * - * Parameters: - * none - * - * Returns: - * The supported API, as specified in the header file OEMCryptoCENC.h. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in each API version. */ uint32_t OEMCrypto_MinorAPIVersion(void); -/* - * OEMCrypto_BuildInformation +/** + * Report the build information of the OEMCrypto library as a short null + * terminated C string. The string should be at most 128 characters long. + * This string should be updated with each release or OEMCrypto build. * - * Description: - * Report the build information of the OEMCrypto library as a short null - * terminated C string. The string should be at most 128 characters long. - * This string should be updated with each release or OEMCrypto build. + * Some SOC vendors deliver a binary OEMCrypto library to a device + * manufacturer. This means the OEMCrypto version may not be exactly in sync + * with the system's versions. This string can be used to help track which + * version is installed on a device. * - * Some SOC vendors deliver a binary OEMCrypto library to a device - * manufacturer. This means the OEMCrypto version may not be exactly in sync - * with the system's versions. This string can be used to help track which - * version is installed on a device. + * It may be used for logging or bug tracking and may be bubbled up to the + * app so that it may track metrics on errors. * - * It may be used for logging or bug tracking and may be bubbled up to the - * app so that it may track metrics on errors. + * Since the OEMCrypto API also changes its minor version number when there + * are minor corrections, it would be useful to include the API version + * number in this string, e.g. "15.1" or "15.2" if those minor versions are + * released. * - * Since the OEMCrypto API also changes its minor version number when there - * are minor corrections, it would be useful to include the API version - * number in this string, e.g. "15.1" or "15.2" if those minor versions are - * released. + * @retval A printable null terminated C string, suitable for a single line in a + * log. * - * Parameters: - * none - * - * Returns: - * A printable null terminated C string, suitable for a single line in a log. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in each API version. */ const char* OEMCrypto_BuildInformation(void); -/* - * OEMCrypto_Security_Patch_Level +/** + * This function returns the current patch level of the software running in + * the trusted environment. The patch level is defined by the OEM, and is + * only incremented when a security update has been added. * - * Description: - * This function returns the current patch level of the software running in - * the trusted environment. The patch level is defined by the OEM, and is - * only incremented when a security update has been added. + * See the section Security Patch Level above for more details. * - * See the section Security Patch Level above for more details. + * @retval The OEM defined version number. * - * Parameters: - * none - * - * Returns: - * The OEM defined version number. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method was introduced in API version 11. */ uint8_t OEMCrypto_Security_Patch_Level(void); -/* - * OEMCrypto_SecurityLevel +/** + * Returns a string specifying the security level of the library. * - * Description: - * Returns a string specifying the security level of the library. + * Since this function is spoofable, it is not relied on for security + * purposes. It is for information only. * - * Since this function is spoofable, it is not relied on for security - * purposes. It is for information only. + * @retval A null terminated string. Useful value are "L1", "L2" and "L3". * - * Parameters: - * none - * - * Returns: - * A null terminated string. Useful value are "L1", "L2" and "L3". - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 6. */ const char* OEMCrypto_SecurityLevel(void); -/* - * OEMCrypto_GetHDCPCapability +/** + * Returns the maximum HDCP version supported by the device, and the HDCP + * version supported by the device and any connected display. * - * Description: - * Returns the maximum HDCP version supported by the device, and the HDCP - * version supported by the device and any connected display. + * Valid values for HDCP_Capability are: * - * Valid values for HDCP_Capability are: + * The value 0xFF means the device is using a local, secure, data path + * instead of HDMI output. Notice that HDCP must use flag Type 1: all + * downstream devices will also use the same version or higher. * - * The value 0xFF means the device is using a local, secure, data path - * instead of HDMI output. Notice that HDCP must use flag Type 1: all - * downstream devices will also use the same version or higher. + * The maximum HDCP level should be the maximum value that the device can + * enforce. For example, if the device has an HDCP 1.0 port and an HDCP 2.0 + * port, and the first port can be disabled, then the maximum is HDCP 2.0. If + * the first port cannot be disabled, then the maximum is HDCP 1.0. The + * maximum value can be used by the application or server to decide if a + * license may be used in the future. For example, a device may be connected + * to an external display while an offline license is downloaded, but the + * user intends to view the content on a local display. The user will want to + * download the higher quality content. * - * The maximum HDCP level should be the maximum value that the device can - * enforce. For example, if the device has an HDCP 1.0 port and an HDCP 2.0 - * port, and the first port can be disabled, then the maximum is HDCP 2.0. If - * the first port cannot be disabled, then the maximum is HDCP 1.0. The - * maximum value can be used by the application or server to decide if a - * license may be used in the future. For example, a device may be connected - * to an external display while an offline license is downloaded, but the - * user intends to view the content on a local display. The user will want to - * download the higher quality content. + * The current HDCP level should be the level of HDCP currently negotiated + * with any connected receivers or repeaters either through HDMI or a + * supported wireless format. If multiple ports are connected, the current + * level should be the minimum HDCP level of all ports. If the key control + * block requires an HDCP level equal to or lower than the current HDCP + * level, the key is expected to be usable. If the key control block requires + * a higher HDCP level, the key is expected to be forbidden. * - * The current HDCP level should be the level of HDCP currently negotiated - * with any connected receivers or repeaters either through HDMI or a - * supported wireless format. If multiple ports are connected, the current - * level should be the minimum HDCP level of all ports. If the key control - * block requires an HDCP level equal to or lower than the current HDCP - * level, the key is expected to be usable. If the key control block requires - * a higher HDCP level, the key is expected to be forbidden. + * When a key has version HDCP_V2_3 required in the key control block, the + * transmitter must have HDCP version 2.3 and have negotiated a connection + * with a version 2.2 or 2.3 receiver or repeater. The transmitter must + * configure the content stream to be Type 1. Since the transmitter cannot + * distinguish between 2.2 and 2.3 downstream receivers when connected to a + * repeater, it may transmit to both 2.2 and 2.3 receivers, but not 2.1 + * receivers. * - * When a key has version HDCP_V2_3 required in the key control block, the - * transmitter must have HDCP version 2.3 and have negotiated a connection - * with a version 2.2 or 2.3 receiver or repeater. The transmitter must - * configure the content stream to be Type 1. Since the transmitter cannot - * distinguish between 2.2 and 2.3 downstream receivers when connected to a - * repeater, it may transmit to both 2.2 and 2.3 receivers, but not 2.1 - * receivers. + * For example, if the transmitter is 2.3, and is connected to a receiver + * that supports 2.3 then the current level is HDCP_V2_3. If the transmitter + * is 2.3 and is connected to a 2.3 repeater, the current level is HDCP_V2_3 + * even though the repeater can negotiate a connection with a 2.2 downstream + * receiver for a Type 1 Content Stream. * - * For example, if the transmitter is 2.3, and is connected to a receiver - * that supports 2.3 then the current level is HDCP_V2_3. If the transmitter - * is 2.3 and is connected to a 2.3 repeater, the current level is HDCP_V2_3 - * even though the repeater can negotiate a connection with a 2.2 downstream - * receiver for a Type 1 Content Stream. + * As another example, if the transmitter can support 2.3, but a receiver + * supports 2.0, then the current level is HDCP_V2. * - * As another example, if the transmitter can support 2.3, but a receiver - * supports 2.0, then the current level is HDCP_V2. + * When a license requires HDCP, a device may use a wireless protocol to + * connect to a display only if that protocol supports the version of HDCP as + * required by the license. Both WirelessHD (formerly WiFi Display) and + * Miracast support HDCP. * - * When a license requires HDCP, a device may use a wireless protocol to - * connect to a display only if that protocol supports the version of HDCP as - * required by the license. Both WirelessHD (formerly WiFi Display) and - * Miracast support HDCP. + * @param[out] current: this is the current HDCP version, based on the device + * itself, and the display to which it is connected. + * @param[out] maximum: this is the maximum supported HDCP version for the + * device, ignoring any attached device. * - * Parameters: - * [out] current - this is the current HDCP version, based on the device - * itself, and the display to which it is connected. - * [out] maximum - this is the maximum supported HDCP version for the device, - * ignoring any attached device. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 10. */ OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability* current, OEMCrypto_HDCP_Capability* maximum); -/* - * OEMCrypto_SupportsUsageTable +/** + * This is used to determine if the device can support a usage table. Since + * this function is spoofable, it is not relied on for security purposes. It + * is for information only. The usage table is described in the section above. * - * Description: - * This is used to determine if the device can support a usage table. Since - * this function is spoofable, it is not relied on for security purposes. It - * is for information only. The usage table is described in the section above. - * - * Parameters: - * none - * - * Returns: - * Returns true if the device can maintain a usage table. Returns false + * @retval Returns true if the device can maintain a usage table. Returns false * otherwise. * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 9. */ bool OEMCrypto_SupportsUsageTable(void); -/* - * OEMCrypto_MaximumUsageTableHeaderSize +/** + * Estimates the maximum usage table size. If the device does not have a + * fixed size, this returns an estimate. A maximum size of 0 means the header + * is constrained only by dynamic memory allocation. * - * Description: - * Estimates the maximum usage table size. If the device does not have a - * fixed size, this returns an estimate. A maximum size of 0 means the header - * is constrained only by dynamic memory allocation. + * Widevine requires the size to be at least 300 entries. * - * Widevine requires the size to be at least 300 entries. + * @retval Returns an estimate for the maximum size of the usage table header. * - * Parameters: - * none - * - * Returns: - * Returns an estimate for the maximum size of the usage table header. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ size_t OEMCrypto_MaximumUsageTableHeaderSize(void); -/* - * OEMCrypto_IsAntiRollbackHwPresent +/** + * Indicate whether there is hardware protection to detect and/or prevent the + * rollback of the usage table. For example, if the usage table contents is + * stored entirely on a secure file system that the user cannot read or write + * to. Another example is if the usage table has a generation number and the + * generation number is stored in secure memory that is not user accessible. * - * Description: - * Indicate whether there is hardware protection to detect and/or prevent the - * rollback of the usage table. For example, if the usage table contents is - * stored entirely on a secure file system that the user cannot read or write - * to. Another example is if the usage table has a generation number and the - * generation number is stored in secure memory that is not user accessible. - * - * Parameters: - * none - * - * Returns: - * Returns true if oemcrypto uses anti-rollback hardware. Returns false + * @retval Returns true if oemcrypto uses anti-rollback hardware. Returns false * otherwise. * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 10. */ bool OEMCrypto_IsAntiRollbackHwPresent(void); -/* - * OEMCrypto_GetNumberOfOpenSessions +/** + * Returns the current number of open sessions. The CDM and OEMCrypto + * consumers can query this value so they can use resources more effectively. * - * Description: - * Returns the current number of open sessions. The CDM and OEMCrypto - * consumers can query this value so they can use resources more effectively. + * @param[out] count: this is the current number of opened sessions. * - * Parameters: - * [out] count - this is the current number of opened sessions. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 10. */ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count); -/* - * OEMCrypto_GetMaxNumberOfSessions +/** + * Returns the maximum number of concurrent OEMCrypto sessions supported by + * the device. The CDM and OEMCrypto consumers can query this value so they + * can use resources more effectively. If the maximum number of sessions + * depends on a dynamically allocated shared resource, the returned value + * should be a best estimate of the maximum number of sessions. * - * Description: - * Returns the maximum number of concurrent OEMCrypto sessions supported by - * the device. The CDM and OEMCrypto consumers can query this value so they - * can use resources more effectively. If the maximum number of sessions - * depends on a dynamically allocated shared resource, the returned value - * should be a best estimate of the maximum number of sessions. + * OEMCrypto shall support a minimum of 10 sessions. Some applications use + * multiple sessions to pre-fetch licenses, so high end devices should + * support more sessions -- we recommend a minimum of 50 sessions. * - * OEMCrypto shall support a minimum of 10 sessions. Some applications use - * multiple sessions to pre-fetch licenses, so high end devices should - * support more sessions -- we recommend a minimum of 50 sessions. + * @param[out] max: this is the max number of supported sessions. * - * Parameters: - * [out] max - this is the max number of supported sessions. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); -/* - * OEMCrypto_SupportedCertificates +/** + * Returns the type of certificates keys that this device supports. With very + * few exceptions, all devices should support at least 2048 bit RSA keys. + * High end devices should also support 3072 bit RSA keys. Devices that are + * cast receivers should also support RSA cast receiver certificates. * - * Description: - * Returns the type of certificates keys that this device supports. With very - * few exceptions, all devices should support at least 2048 bit RSA keys. - * High end devices should also support 3072 bit RSA keys. Devices that are - * cast receivers should also support RSA cast receiver certificates. + * Beginning with OEMCrypto v14, the provisioning server may deliver to the + * device an RSA key that uses the Carmichael totient. This does not change + * the RSA algorithm -- however the product of the private and public keys is + * not necessarily the Euler number \phi (n). OEMCrypto should not reject + * such keys. * - * Beginning with OEMCrypto v14, the provisioning server may deliver to the - * device an RSA key that uses the Carmichael totient. This does not change - * the RSA algorithm -- however the product of the private and public keys is - * not necessarily the Euler number \phi (n). OEMCrypto should not reject - * such keys. - * - * Parameters: - * none - * - * Returns: + * @return * Returns the bitwise or of the following flags. It is likely that high end * devices will support both 2048 and 3072 bit keys while the widevine * servers transition to new key sizes. @@ -3592,367 +3494,343 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); * - 0x200 = OEMCrypto_Supports_ECC_secp384r1 - Elliptic Curve secp384r1 * - 0x400 = OEMCrypto_Supports_ECC_secp521r1 - Elliptic Curve secp521r1 * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ uint32_t OEMCrypto_SupportedCertificates(void); -/* - * OEMCrypto_IsSRMUpdateSupported +/** + * Returns true if the device supports SRM files and the file can be updated + * via the function OEMCrypto_LoadSRM. This also returns false for devices + * that do not support an SRM file, devices that do not support HDCP, and + * devices that have no external display support. * - * Description: - * Returns true if the device supports SRM files and the file can be updated - * via the function OEMCrypto_LoadSRM. This also returns false for devices - * that do not support an SRM file, devices that do not support HDCP, and - * devices that have no external display support. + * @retval true if LoadSRM is supported. + * @retval false otherwise. * - * Parameters: - * none - * - * Returns: - * true - if LoadSRM is supported. - * false - otherwise. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ bool OEMCrypto_IsSRMUpdateSupported(void); -/* - * OEMCrypto_GetCurrentSRMVersion +/** + * Returns the version number of the current SRM file. If the device does not + * support SRM files, this will return OEMCrypto_ERROR_NOT_IMPLEMENTED. If + * the device only supports local displays, it would return + * OEMCrypto_LOCAL_DISPLAY_ONLY. If the device has an SRM, but cannot use + * OEMCrypto to update the SRM, then this function would set version to be + * the current version number, and return OEMCrypto_SUCCESS, but it would + * return false from OEMCrypto_IsSRMUpdateSupported. * - * Description: - * Returns the version number of the current SRM file. If the device does not - * support SRM files, this will return OEMCrypto_ERROR_NOT_IMPLEMENTED. If - * the device only supports local displays, it would return - * OEMCrypto_LOCAL_DISPLAY_ONLY. If the device has an SRM, but cannot use - * OEMCrypto to update the SRM, then this function would set version to be - * the current version number, and return OEMCrypto_SUCCESS, but it would - * return false from OEMCrypto_IsSRMUpdateSupported. + * @param[out] version: current SRM version number. * - * Parameters: - * [out] version: current SRM version number. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_LOCAL_DISPLAY_ONLY to indicate version was not set, and + * is not needed. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_SUCCESS - * OEMCrypto_LOCAL_DISPLAY_ONLY - to indicate version was not set, and is not - * needed. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version); -/* - * OEMCrypto_GetAnalogOutputFlags +/** + * Returns whether the device supports analog output or not. This information + * will be sent to the license server, and may be used to determine the type + * of license allowed. This function is for reporting only. It is paired with + * the key control block flags Disable_Analog_Output and CGMS. * - * Description: - * Returns whether the device supports analog output or not. This information - * will be sent to the license server, and may be used to determine the type - * of license allowed. This function is for reporting only. It is paired with - * the key control block flags Disable_Analog_Output and CGMS. - * - * Parameters: - * none. - * - * Returns: - * Returns a bitwise OR of the following flags. - * - 0x0 = OEMCrypto_No_Analog_Output -- the device has no analog output. - * - 0x1 = OEMCrypto_Supports_Analog_Output - the device does have analog + * @return + * Returns a bitwise OR of all possible return values. + * * 0x0 = OEMCrypto_No_Analog_Output: the device has no analog output. + * * 0x1 = OEMCrypto_Supports_Analog_Output: the device does have analog * output. - * - 0x2 = OEMCrypto_Can_Disable_Analog_Ouptput - the device does have + * * 0x2 = OEMCrypto_Can_Disable_Analog_Ouptput: the device does have * analog output, but it will disable analog output if required by the * key control block. - * - 0x4 = OEMCrypto_Supports_CGMS_A - the device supports signaling 2-bit + * * 0x4 = OEMCrypto_Supports_CGMS_A: the device supports signaling 2-bit * CGMS-A, if required by the key control block * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 14. */ uint32_t OEMCrypto_GetAnalogOutputFlags(void); -/* - * OEMCrypto_ResourceRatingTier +/** + * This function returns a positive number indicating which resource rating + * it supports. This value will bubble up to the application level as a + * property. This will allow applications to estimate what resolution and + * bandwidth the device is expected to support. * - * Description: - * This function returns a positive number indicating which resource rating - * it supports. This value will bubble up to the application level as a - * property. This will allow applications to estimate what resolution and - * bandwidth the device is expected to support. + * OEMCrypto unit tests and Android GTS tests will verify that devices do + * support the resource values specified in the table below at the tier + * claimed by the device. If a device claims to be a low end device, the + * OEMCrypto unit tests will only verify the low end performance values. * - * OEMCrypto unit tests and Android GTS tests will verify that devices do - * support the resource values specified in the table below at the tier - * claimed by the device. If a device claims to be a low end device, the - * OEMCrypto unit tests will only verify the low end performance values. + * OEMCrypto implementers should consider the numbers below to be minimum + * values. * - * OEMCrypto implementers should consider the numbers below to be minimum - * values. + * These performance parameters are for OEMCrypto only. In particular, + * bandwidth and codec resolution are determined by the platform. * - * These performance parameters are for OEMCrypto only. In particular, - * bandwidth and codec resolution are determined by the platform. + * Some parameters need more explanation. The Sample size is typically the + * size of one encoded frame, but might be several frames for AV1. Converting + * this to resolution depends on the Codec, which is not specified by + * OEMCrypto. Some content has the sample broken into several subsamples. The + * "number of subsamples" restriction requires that any content can be broken + * into at least that many subsamples. However, this number may be larger if + * DecryptCENC returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. In that case, the + * layer above OEMCrypto will break the sample into subsamples of size + * "Decrypt Buffer Size" as specified in the table below. The "Decrypt Buffer + * Size" means the size of one subsample that may be passed into DecryptCENC + * or CopyBuffer without returning error OEMCrypto_ERROR_BUFFER_TOO_LARGE. * - * Some parameters need more explanation. The Sample size is typically the - * size of one encoded frame, but might be several frames for AV1. Converting - * this to resolution depends on the Codec, which is not specified by - * OEMCrypto. Some content has the sample broken into several subsamples. The - * "number of subsamples" restriction requires that any content can be broken - * into at least that many subsamples. However, this number may be larger if - * DecryptCENC returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. In that case, the - * layer above OEMCrypto will break the sample into subsamples of size - * "Decrypt Buffer Size" as specified in the table below. The "Decrypt Buffer - * Size" means the size of one subsample that may be passed into DecryptCENC - * or CopyBuffer without returning error OEMCrypto_ERROR_BUFFER_TOO_LARGE. + * The minimum subsample buffer size is the smallest buffer that the CDM + * layer above OEMCrypto will use when breaking a sample into subsamples. As + * mentioned above, the CDM layer will only break a sample into smaller + * subsamples if OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. Because + * this might be a performance problem, OEMCrypto implementers are encouraged + * to process larger subsamples and to process multiple subsamples in a + * single call to DecryptCENC. * - * The minimum subsample buffer size is the smallest buffer that the CDM - * layer above OEMCrypto will use when breaking a sample into subsamples. As - * mentioned above, the CDM layer will only break a sample into smaller - * subsamples if OEMCrypto returns OEMCrypto_ERROR_BUFFER_TOO_LARGE. Because - * this might be a performance problem, OEMCrypto implementers are encouraged - * to process larger subsamples and to process multiple subsamples in a - * single call to DecryptCENC. + * The number of keys per session is an indication of how many different + * track types there can be for a piece of content. Typically, content will + * have several keys corresponding to audio and video at different + * resolutions. If the content uses key rotation, there could be three keys + * -- previous interval, current interval, and next interval -- for each + * resolution. * - * The number of keys per session is an indication of how many different - * track types there can be for a piece of content. Typically, content will - * have several keys corresponding to audio and video at different - * resolutions. If the content uses key rotation, there could be three keys - * -- previous interval, current interval, and next interval -- for each - * resolution. + * Concurrent playback sessions versus concurrent sessions: some applications + * will preload multiple licenses before the user picks which content to + * play. Each of these licenses corresponds to an open session. Once playback + * starts, some platforms support picture-in-picture or multiple displays. + * Each of these pictures would correspond to a separate playback session + * with active decryption. * - * Concurrent playback sessions versus concurrent sessions: some applications - * will preload multiple licenses before the user picks which content to - * play. Each of these licenses corresponds to an open session. Once playback - * starts, some platforms support picture-in-picture or multiple displays. - * Each of these pictures would correspond to a separate playback session - * with active decryption. + * The total number of keys for all sessions indicates that the device may + * share key memory over multiple sessions. For example, on a Tier 3 device, + * the device must support four sessions with 20 keys each (80 total), or 20 + * sessions with 4 keys each (80 total), but it does not need to support 20 + * sessions with 20 keys each. * - * The total number of keys for all sessions indicates that the device may - * share key memory over multiple sessions. For example, on a Tier 3 device, - * the device must support four sessions with 20 keys each (80 total), or 20 - * sessions with 4 keys each (80 total), but it does not need to support 20 - * sessions with 20 keys each. + * The message size that is needed for a license with a large number of keys + * is larger than in previous versions. The message size limit applies to all + * functions that sign or verify messages. It also applies to the size of + * context buffers in the derive key functions. * - * The message size that is needed for a license with a large number of keys - * is larger than in previous versions. The message size limit applies to all - * functions that sign or verify messages. It also applies to the size of - * context buffers in the derive key functions. + * Decrypted frames per second -- strictly speaking, OEMCrypto only controls + * the decryption part of playback and cannot control the decoding and + * display part. However, devices that support the higher resource tiers + * should also support a higher frame rate. Platforms may enforce these + * values. For example Android will enforce a frame rate via a GTS test. * - * Decrypted frames per second -- strictly speaking, OEMCrypto only controls - * the decryption part of playback and cannot control the decoding and - * display part. However, devices that support the higher resource tiers - * should also support a higher frame rate. Platforms may enforce these - * values. For example Android will enforce a frame rate via a GTS test. + * Note on units: We will use KiB to mean 1024 bytes and MiB to mean 1024 + * KiB, as described at https://en.wikipedia.org/wiki/Kibibyte. * - * Note on units: We will use KiB to mean 1024 bytes and MiB to mean 1024 - * KiB, as described at https://en.wikipedia.org/wiki/Kibibyte. + *

+ * +--------------------------------+---------+----------+---------+---------+
+ * |Resource Rating Tier            |1 - Low  |2 - Medium|3 - High |4 - Very |
+ * |                                |         |          |         |    High |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Sample size             |1 MiB    |2 MiB     |4 MiB    |16 MiB   |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Number of Subsamples    |10       |16        |32       |64       |
+ * | (H264 or HEVC)                 |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Number of Subsamples    |9        |9         |9        |9        |
+ * |(VP9)                           |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Number of Subsamples    |72       |144       |288      |576      |
+ * |(AV1)                           |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum subsample buffer size   |100 KiB  |500 KiB   |1 MiB    |4 MiB    |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Generic crypto buffer   |10 KiB   |100 KiB   |500 KiB  |1 MiB    |
+ * |size                            |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum number of concurrent    |10       |20        |30       |40       |
+ * |sessions                        |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum number of keys per      |4        |20        |20       |30       |
+ * |session                         |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum total number of keys    |16       |40        |80       |90       |
+ * | (all sessions)                 |         |          |         |         |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Minimum Message Size            |8 KiB    |8 KiB     |16 KiB   |32 KiB   |
+ * +--------------------------------+---------+----------+---------+---------+
+ * |Decrypted Frames per Second     |30 fps SD|30 fps HD |60 fps HD|60 fps 8k|
+ * +--------------------------------+---------+----------+---------+---------+
+ * 
* - * +--------------------------------+---------+----------+---------+---------+ - * |Resource Rating Tier |1 - Low |2 - Medium|3 - High |4 - Very | - * | | | | | High | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Sample size |1 MiB |2 MiB |4 MiB |16 MiB | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Number of Subsamples |10 |16 |32 |64 | - * | (H264 or HEVC) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Number of Subsamples |9 |9 |9 |9 | - * |(VP9) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Number of Subsamples |72 |144 |288 |576 | - * |(AV1) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum subsample buffer size |100 KiB |500 KiB |1 MiB |4 MiB | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Generic crypto buffer |10 KiB |100 KiB |500 KiB |1 MiB | - * |size | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum number of concurrent |10 |20 |30 |40 | - * |sessions | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum number of keys per |4 |20 |20 |30 | - * |session | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum total number of keys |16 |40 |80 |90 | - * | (all sessions) | | | | | - * +--------------------------------+---------+----------+---------+---------+ - * |Minimum Message Size |8 KiB |8 KiB |16 KiB |32 KiB | - * +--------------------------------+---------+----------+---------+---------+ - * |Decrypted Frames per Second |30 fps SD|30 fps HD |60 fps HD|60 fps 8k| - * +--------------------------------+---------+----------+---------+---------+ + * @return + * Returns an integer indicating which resource tier the device supports. * - * Parameters: - * none. - * - * Returns: - * Returns an integer indicating which resource tier the device supports. - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 15. */ uint32_t OEMCrypto_ResourceRatingTier(void); -/* - * OEMCrypto_LoadProvisioning +/// @} + +/// @addtogroup drm_cert +/// @{ + +/** + * Load and parse a provisioning response, and then rewrap the private key + * for storage on the filesystem. We recommend that the OEM use an encryption + * key and signing key generated using an algorithm at least as strong as + * that in GenerateDerivedKeys. * - * Description: - * Load and parse a provisioning response, and then rewrap the private key - * for storage on the filesystem. We recommend that the OEM use an encryption - * key and signing key generated using an algorithm at least as strong as - * that in GenerateDerivedKeys. + * First, OEMCrypto shall verify the signature of the message using + * HMAC-SHA256 with the derived mac_key[server]. The signature verification + * shall use a constant-time algorithm (a signature mismatch will always take + * the same time as a successful comparison). The signature is over the + * entire message buffer starting at message with length message_length. If + * the signature verification fails, ignore all other arguments and return + * OEMCrypto_ERROR_SIGNATURE_FAILURE. * - * First, OEMCrypto shall verify the signature of the message using - * HMAC-SHA256 with the derived mac_key[server]. The signature verification - * shall use a constant-time algorithm (a signature mismatch will always take - * the same time as a successful comparison). The signature is over the - * entire message buffer starting at message with length message_length. If - * the signature verification fails, ignore all other arguments and return - * OEMCrypto_ERROR_SIGNATURE_FAILURE. + * NOTE: The calling software must have previously established the mac_keys + * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey or + * OEMCrypto_GenerateDerivedKeys. * - * NOTE: The calling software must have previously established the mac_keys - * and encrypt_key with a call to OEMCrypto_DeriveKeysFromSessionKey or - * OEMCrypto_GenerateDerivedKeys. + * The function ODK_ParseProvisioning is called to parse the message. If it + * returns an error, OEMCrypto shall return that error to the CDM layer. The + * function ODK_ParseProvisioning is described in the document "Widevine Core + * Message Serialization". * - * The function ODK_ParseProvisioning is called to parse the message. If it - * returns an error, OEMCrypto shall return that error to the CDM layer. The - * function ODK_ParseProvisioning is described in the document "Widevine Core - * Message Serialization". + * Below, all fields are found in the struct ODK_ParsedLicense parsed_license + * returned by ODK_ParsedProvisioning. * - * Below, all fields are found in the struct ODK_ParsedLicense parsed_license - * returned by ODK_ParsedProvisioning. + * After decrypting `parsed_response->enc_private_key`, If the first four bytes + * of the buffer are the string "SIGN", then the actual RSA key begins on the + * 9th byte of the buffer. The second four bytes of the buffer is the 32 bit + * field "allowed_schemes" of type RSA_Padding_Scheme, which is used in + * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must also be + * wrapped with RSA key. We recommend storing the magic string "SIGN" with + * the key to distinguish keys that have a value for allowed_schemes from + * those that should use the default allowed_schemes. Devices that do not + * support the alternative signing algorithms may refuse to load these keys + * and return an error of OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case + * for these alternative signing algorithms is to support devices that use + * X509 certificates for authentication when acting as a ChromeCast receiver. + * This is not needed for devices that wish to send data to a ChromeCast. * - * After decrypting enc_rsa_key, If the first four bytes of the buffer are - * the string "SIGN", then the actual RSA key begins on the 9th byte of the - * buffer. The second four bytes of the buffer is the 32 bit field - * "allowed_schemes"of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must also be - * wrapped with RSA key. We recommend storing the magic string "SIGN" with - * the key to distinguish keys that have a value for allowed_schemes from - * those that should use the default allowed_schemes. Devices that do not - * support the alternative signing algorithms may refuse to load these keys - * and return an error of OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case - * for these alternative signing algorithms is to support devices that use - * X509 certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * - * If the first four bytes of the buffer enc_rsa_key are not the string - * "SIGN", then the default value of allowed_schemes = 1 (kSign_RSASSA_PSS) - * will be used. + * If the first four bytes of the buffer `enc_private_key` are not the string + * "SIGN", then this key may not be used with OEMCrypto_GenerateRSASignature. * * Verification and Algorithm: - * The following checks should be performed. If any check fails, an error is - * returned, and the key is not loaded. - * 1. Check that all the pointer values passed into it are within the - * buffer specified by message and message_length. - * 2. Verify that in_wrapped_rsa_key_length is large enough to hold the - * rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. - * 3. Verify the message signature, using the derived signing key - * (mac_key[server]) from a previous call to - * OEMCrypto_GenerateDerivedKeys or OEMCrypto_DeriveKeysFromSessionKey. - * 4. The function ODK_ParseProvisioning is called to parse the message. - * 5. Decrypt enc_rsa_key in the buffer rsa_key using the session's derived - * encryption key (enc_key). Use enc_rsa_key_iv as the initial vector - * for AES_128-CBC mode, with PKCS#5 padding. The rsa_key should be kept - * in secure memory and protected from the user. - * 6. If the first four bytes of the buffer rsa_key are the string "SIGN", - * then the actual RSA key begins on the 9th byte of the buffer. The - * second four bytes of the buffer is the 32 bit field - * "allowed_schemes", of type RSA_Padding_Scheme, which is used in - * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must - * also be wrapped with RSA key. We recommend storing the magic string - * "SIGN" with the key to distinguish keys that have a value for - * allowed_schemes from those that should use the default - * allowed_schemes. Devices that do not support the alternative signing - * algorithms may refuse to load these keys and return an error of - * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these - * alternative signing algorithms is to support devices that use X.509 - * certificates for authentication when acting as a ChromeCast receiver. - * This is not needed for devices that wish to send data to a ChromeCast. - * 7. If the first four bytes of the buffer rsa_key are not the string - * "SIGN", then the default value of allowed_schemes = 1 - * (kSign_RSASSA_PSS) will be used. - * 8. After possibly skipping past the first 8 bytes signifying the allowed - * signing algorithm, the rest of the buffer rsa_key contains an RSA - * device key in PKCS#8 binary DER encoded format. The OEMCrypto library - * shall verify that this RSA key is valid. - * 9. Re-encrypt the device RSA key with an internal key (such as the OEM - * key or Widevine Keybox key) and the generated IV using AES-128-CBC - * with PKCS#5 padding. - * 10. Copy the rewrapped key to the buffer specified by wrapped_rsa_key - * and the size of the wrapped key to wrapped_rsa_key_length. + * The following checks should be performed. If any check fails, an error is + * returned, and the key is not loaded. + * 1. Check that all the pointer values passed into it are within the + * buffer specified by message and message_length. + * 2. Verify that (in) wrapped_private_key_length is large enough to hold + * the rewrapped key, returning OEMCrypto_ERROR_SHORT_BUFFER otherwise. + * 3. Verify the message signature, using the derived signing key + * (mac_key[server]) from a previous call to + * OEMCrypto_GenerateDerivedKeys or OEMCrypto_DeriveKeysFromSessionKey. + * 4. The function ODK_ParseProvisioning is called to parse the message. + * 5. Decrypt enc_private_key in the buffer private_key using the session's + * derived encryption key (enc_key). Use enc_private_key_iv as the initial + * vector for AES_128-CBC mode, with PKCS#5 padding. The private_key should + * be kept in secure memory and protected from the user. + * 6. If the first four bytes of the buffer private_key are the string "SIGN", + * then the actual RSA key begins on the 9th byte of the buffer. The + * second four bytes of the buffer is the 32 bit field + * "allowed_schemes", of type RSA_Padding_Scheme, which is used in + * OEMCrypto_GenerateRSASignature. The value of allowed_schemes must + * also be wrapped with RSA key. We recommend storing the magic string + * "SIGN" with the key to distinguish keys that have a value for + * allowed_schemes from those that should use the default + * allowed_schemes. Devices that do not support the alternative signing + * algorithms may refuse to load these keys and return an error of + * OEMCrypto_ERROR_NOT_IMPLEMENTED. The main use case for these + * alternative signing algorithms is to support devices that use X.509 + * certificates for authentication when acting as a ChromeCast receiver. + * This is not needed for devices that wish to send data to a ChromeCast. + * 7. If the first four bytes of the buffer private_key are not the string + * "SIGN", this key may not be used with OEMCrypto_GenerateRSASignature. + * 8. After possibly skipping past the first 8 bytes signifying the allowed + * signing algorithm, the rest of the buffer private_key contains an ECC + * private key or an RSA private key in PKCS#8 binary DER encoded + * format. The OEMCrypto library shall verify that this private key is + * valid. + * 9. Re-encrypt the device private key with an internal key (such as the OEM + * key or Widevine Keybox key) and the generated IV using AES-128-CBC + * with PKCS#5 padding. + * 10. Copy the rewrapped key to the buffer specified by wrapped_private_key + * and the size of the wrapped key to wrapped_private_key_length. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing data. - * [in] message_length: length of the message, in bytes. - * [in] core_message_length: length of the core submessage, in bytes. - * [in] signature: pointer to memory containing the signature. - * [in] signature_length: length of the signature, in bytes. - * [out] wrapped_private_key: pointer to buffer in which encrypted RSA or ECC - * private key should be stored. May be null on the first call in order - * to find required buffer size. - * [in/out] wrapped_private_key_length: (in) length of the encrypted private - * key, in bytes. - * (out) actual length of the encrypted private key + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing data. + * @param[in] message_length: length of the message, in bytes. + * @param[in] core_message_length: length of the core submessage, in bytes. + * @param[in] signature: pointer to memory containing the signature. + * @param[in] signature_length: length of the signature, in bytes. + * @param[out] wrapped_private_key: pointer to buffer in which encrypted RSA or + * ECC private key should be stored. May be null on the first call in order + * to find required buffer size. + * @param[in,out] wrapped_private_key_length: (in) length of the encrypted + * private key, in bytes. (out) actual length of the encrypted private key * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_INVALID_NONCE - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE + * @retval OEMCrypto_ERROR_INVALID_NONCE + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadProvisioning( @@ -3961,23 +3839,20 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( size_t signature_length, uint8_t* wrapped_private_key, size_t* wrapped_private_key_length); -/* - * OEMCrypto_LoadDRMPrivateKey +/** + * Loads a wrapped RSA or ECC private key to secure memory for use by this + * session in future calls to OEMCrypto_PrepAndSignLicenseRequest or + * OEMCrypto_DeriveKeysFromSessionKey. The wrapped private key will be the + * one verified and wrapped by OEMCrypto_LoadProvisioning. The private key + * should be stored in secure memory. * - * Description: - * Loads a wrapped RSA or ECC private key to secure memory for use by this - * session in future calls to OEMCrypto_PrepAndSignLicenseRequest or - * OEMCrypto_DeriveKeysFromSessionKey. The wrapped private key will be the - * one verified and wrapped by OEMCrypto_LoadProvisioning. The private key - * should be stored in secure memory. + * If the bit field "allowed_schemes" was wrapped with this RSA key, its + * value will be loaded and stored with the RSA key, and the key may be used + * with calls to OEMCrypto_GenerateRSASignature. If there was not a bit field + * wrapped with the RSA key, the key will be used for + * OEMCrypto_PrepAndSignLicenseRequest or OEMCrypto_DeriveKeysFromSessionKey * - * If the bit field "allowed_schemes" was wrapped with this RSA key, its - * value will be loaded and stored with the RSA key, and the key may be used - * with calls to OEMCrypto_GenerateRSASignature. If there was not a bit field - * wrapped with the RSA key, the key will be used for - * OEMCrypto_PrepAndSignLicenseRequest or OEMCrypto_DeriveKeysFromSessionKey - * - * Verification: + * @verification * The following checks should be performed. If any check fails, an error is * returned, and the RSA key is not loaded. * 1. The wrapped key has a valid signature, as described in @@ -3986,151 +3861,136 @@ OEMCryptoResult OEMCrypto_LoadProvisioning( * 3. If a value for allowed_schemes is included with the key, it is a * valid value. * - * Parameters: - * [in] session: crypto session identifier. - * [in] key_type: indicates either an RSA or ECC key for devices that support - * both. - * [in] wrapped_rsa_key: wrapped device RSA key stored on the device. Format - * is PKCS#8, binary DER encoded, and encrypted with a key internal to - * the OEMCrypto instance, using AES-128-CBC with PKCS#5 padding. This - * is the wrapped key generated by OEMCrypto_RewrapDeviceRSAKey. - * [in] wrapped_rsa_key_length: length of the wrapped key buffer, in bytes. + * @param[in] session: crypto session identifier. + * @param[in] key_type: indicates either an RSA or ECC key for devices that + * support both. + * @param[in] wrapped_private_key: wrapped device private key (RSA or ECC). + * This is the wrapped key generated by OEMCrypto_LoadProvisioning. + * @param[in] wrapped_private_key_length: length of the wrapped key buffer, in + * bytes. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NO_DEVICE_KEY - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NO_DEVICE_KEY + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadDRMPrivateKey(OEMCrypto_SESSION session, OEMCrypto_PrivateKeyType key_type, - const uint8_t* wrapped_rsa_key, - size_t wrapped_rsa_key_length); + const uint8_t* wrapped_private_key, + size_t wrapped_private_key_length); -/* - * OEMCrypto_LoadTestRSAKey +/** + * Some platforms do not support keyboxes or OEM Certificates. On those + * platforms, there is a DRM certificate baked into the OEMCrypto library. + * This is unusual, and is only available for L3 devices. In order to debug + * and test those devices, they should be able to switch to the test DRM + * certificate. * - * Description: - * Some platforms do not support keyboxes or OEM Certificates. On those - * platforms, there is a DRM certificate baked into the OEMCrypto library. - * This is unusual, and is only available for L3 devices. In order to debug - * and test those devices, they should be able to switch to the test DRM - * certificate. + * Temporarily use the standard test RSA key until the next call to + * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run + * on a production device without permanently changing the key. Using the + * test key is not persistent. * - * Temporarily use the standard test RSA key until the next call to - * OEMCrypto_Terminate. This allows a standard suite of unit tests to be run - * on a production device without permanently changing the key. Using the - * test key is not persistent. + * The test key can be found in the unit test code, oemcrypto_test.cpp, in + * PKCS8 form as the constant kTestRSAPKCS8PrivateKeyInfo2_2048. * - * The test key can be found in the unit test code, oemcrypto_test.cpp, in - * PKCS8 form as the constant kTestRSAPKCS8PrivateKeyInfo2_2048. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED devices that use a keybox should + * not implement this function + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_NOT_IMPLEMENTED - devices that use a keybox should not - * implement this function - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 10. */ OEMCryptoResult OEMCrypto_LoadTestRSAKey(void); -/* - * OEMCrypto_GenerateRSASignature +/** + * The OEMCrypto_GenerateRSASignature method is only used for devices that + * are CAST receivers. This function is called after + * OEMCrypto_LoadDRMPrivateKey for the same session. * - * Description: - * The OEMCrypto_GenerateRSASignature method is only used for devices that - * are CAST receivers. This function is called after - * OEMCrypto_LoadDRMPrivateKey for the same session. + * The parameter padding_scheme has two possible legacy values: * - * The parameter padding_scheme has two possible legacy values: + * 0x1 - RSASSA-PSS with SHA1. * - * 0x1 - RSASSA-PSS with SHA1. + * 0x2 - PKCS1 with block type 1 padding (only). * - * 0x2 - PKCS1 with block type 1 padding (only). + * The only supported padding scheme is 0x2 since version 16 of this API. In + * this second case, the "message" is already a digest, so no further hashing + * is applied, and the message_length can be no longer than 83 bytes. If the + * message_length is greater than 83 bytes OEMCrypto_ERROR_SIGNATURE_FAILURE + * shall be returned. * - * The only supported padding scheme is 0x2 since version 16 of this API. In - * this second case, the "message" is already a digest, so no further hashing - * is applied, and the message_length can be no longer than 83 bytes. If the - * message_length is greater than 83 bytes OEMCrypto_ERROR_SIGNATURE_FAILURE - * shall be returned. + * The second padding scheme is for devices that use X509 certificates for + * authentication. The main example is devices that work as a Cast receiver, + * like a ChromeCast, not for devices that wish to send to the Cast device, + * such as almost all Android devices. OEMs that do not support X509 + * certificate authentication need not implement this function and can return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * The second padding scheme is for devices that use X509 certificates for - * authentication. The main example is devices that work as a Cast receiver, - * like a ChromeCast, not for devices that wish to send to the Cast device, - * such as almost all Android devices. OEMs that do not support X509 - * certificate authentication need not implement this function and can return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * Verification: + * @verification * Both the padding_scheme and the RSA key's allowed_schemes must be 0x2. If * not, then the signature is not computed and the error * OEMCrypto_ERROR_INVALID_RSA_KEY is returned. * - * Parameters: - * [in] session: crypto session identifier. - * [in] message: pointer to memory containing message to be signed. - * [in] message_length: length of the message, in bytes. - * [out] signature: buffer to hold the message signature. On return, it will - * contain the message signature generated with the device private RSA - * key using RSASSA-PSS. Will be null on the first call in order to - * find required buffer size. - * [in/out] signature_length: (in) length of the signature buffer, in bytes. - * (out) actual length of the signature - * [in] padding_scheme: specify which scheme to use for the signature. + * @param[in] session: crypto session identifier. + * @param[in] message: pointer to memory containing message to be signed. + * @param[in] message_length: length of the message, in bytes. + * @param[out] signature: buffer to hold the message signature. On return, it + * will contain the message signature generated with the device private RSA + * key using RSASSA-PSS. Will be null on the first call in order to find + * required buffer size. + * @param[in,out] signature_length: (in) length of the signature buffer, in + * bytes. (out) actual length of the signature + * @param[in] padding_scheme: specify which scheme to use for the signature. * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. - * OEMCrypto_ERROR_INVALID_SESSION - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_INVALID_RSA_KEY - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_NOT_IMPLEMENTED - if algorithm > 0, and the device does - * not support that algorithm. - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if the signature buffer is too small. + * @retval OEMCrypto_ERROR_INVALID_SESSION + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_INVALID_RSA_KEY + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED if algorithm > 0, and the device + * does not support that algorithm. + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support message sizes as described in the section * OEMCrypto_ResourceRatingTier. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_GenerateRSASignature( @@ -4138,196 +3998,181 @@ OEMCryptoResult OEMCrypto_GenerateRSASignature( uint8_t* signature, size_t* signature_length, RSA_Padding_Scheme padding_scheme); -/* - * OEMCrypto_CreateUsageTableHeader +/// @} + +/// @addtogroup usage_table +/// @{ + +/** + * This creates a new Usage Table Header with no entries. If there is already + * a generation number stored in secure storage, it will be incremented by 1 + * and used as the new Master Generation Number. This will only be called if + * the CDM layer finds no existing usage table on the file system. OEMCrypto + * will encrypt and sign the new, empty, header and return it in the provided + * buffer. * - * Description: - * This creates a new Usage Table Header with no entries. If there is already - * a generation number stored in secure storage, it will be incremented by 1 - * and used as the new Master Generation Number. This will only be called if - * the CDM layer finds no existing usage table on the file system. OEMCrypto - * will encrypt and sign the new, empty, header and return it in the provided - * buffer. + * The new entry should be created with a status of kUnused and all times + * times should be set to 0. * - * The new entry should be created with a status of kUnused and all times - * times should be set to 0. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[out] header_buffer: pointer to memory where encrypted usage table + * header is written. + * @param[in,out] header_buffer_length: (in) length of the header_buffer, in + * bytes. (out) actual length of the header_buffer * - * Parameters: - * [out] header_buffer: pointer to memory where encrypted usage table header - * is written. - * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. - * (out) actual length of the header_buffer + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if header_buffer_length is too small. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - if header_buffer_length is too small. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, size_t* header_buffer_length); -/* - * OEMCrypto_LoadUsageTableHeader +/** + * This loads the Usage Table Header. The buffer's signature is verified and + * the buffer is decrypted. OEMCrypto will verify the verification string. If + * the Master Generation Number is more than 1 off, the table is considered + * bad, the headers are NOT loaded, and the error + * OEMCrypto_ERROR_GENERATION_SKEW is returned. If the generation number is + * off by 1, the warning OEMCrypto_WARNING_GENERATION_SKEW is returned but + * the header is still loaded. This warning may be logged by the CDM layer. * - * Description: - * This loads the Usage Table Header. The buffer's signature is verified and - * the buffer is decrypted. OEMCrypto will verify the verification string. If - * the Master Generation Number is more than 1 off, the table is considered - * bad, the headers are NOT loaded, and the error - * OEMCrypto_ERROR_GENERATION_SKEW is returned. If the generation number is - * off by 1, the warning OEMCrypto_WARNING_GENERATION_SKEW is returned but - * the header is still loaded. This warning may be logged by the CDM layer. + * @param[in] buffer: pointer to memory containing encrypted usage table header. + * @param[in] buffer_length: length of the buffer, in bytes. * - * Parameters: - * [in] buffer: pointer to memory containing encrypted usage table header. - * [in] buffer_length: length of the buffer, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by - * exactly 1. - * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more - * than 1. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. - * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_WARNING_GENERATION_SKEW if the generation number is off + * by exactly 1. + * @retval OEMCrypto_ERROR_GENERATION_SKEW if the generation number is off by + * more than 1. + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature failed. + * @retval OEMCrypto_ERROR_BAD_MAGIC verification string does not match. + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_CreateNewUsageEntry +/** + * This creates a new usage entry. The size of the header will be increased + * by 8 bytes, and secure volatile memory will be allocated for it. The new + * entry will be associated with the given session. The status of the new + * entry will be set to "unused". OEMCrypto will set *usage_entry_number to + * be the index of the new entry. The first entry created will have index 0. + * The new entry will be initialized with a generation number equal to the + * master generation number, which will also be stored in the header's new + * slot. Then the master generation number will be incremented. Since each + * entry's generation number is less than the master generation number, the + * new entry will have a generation number that is larger than all other + * entries and larger than all previously deleted entries. This helps prevent + * a rogue application from deleting an entry and then loading an old version + * of it. * - * Description: - * This creates a new usage entry. The size of the header will be increased - * by 8 bytes, and secure volatile memory will be allocated for it. The new - * entry will be associated with the given session. The status of the new - * entry will be set to "unused". OEMCrypto will set *usage_entry_number to - * be the index of the new entry. The first entry created will have index 0. - * The new entry will be initialized with a generation number equal to the - * master generation number, which will also be stored in the header's new - * slot. Then the master generation number will be incremented. Since each - * entry's generation number is less than the master generation number, the - * new entry will have a generation number that is larger than all other - * entries and larger than all previously deleted entries. This helps prevent - * a rogue application from deleting an entry and then loading an old version - * of it. + * If the session already has a usage entry associated with it, the error + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. * - * If the session already has a usage entry associated with it, the error - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. + * @param[in] session: handle for the session to be used. + * @param[out] usage_entry_number: index of new usage entry. * - * Parameters: - * [in] session: handle for the session to be used. - * [out] usage_entry_number: index of new usage entry. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - if there is no room in memory to - * increase the size of the usage table header. The CDM layer can - * delete some entries and then try again, or it can pass the error up - * to the application. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES - if there already is a usage entry - * loaded into this session + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES if there is no room in + * memory to increase the size of the usage table header. The CDM layer + * can delete some entries and then try again, or it can pass the error + * up to the application. + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES if there already is a usage + * entry loaded into this session * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, uint32_t* usage_entry_number); -/* - * OEMCrypto_LoadUsageEntry +/** + * This loads a usage entry saved previously by UpdateUsageEntry. The + * signature at the beginning of the buffer is verified and the buffer will + * be decrypted. Then the verification field in the entry will be verified. + * The index in the entry must match the index passed in. The generation + * number in the entry will be compared against the entry's corresponding + * generation number in the header. If it is off by 1, a warning is returned, + * but the entry is still loaded. This warning may be logged by the CDM + * layer. If the generation number is off by more than 1, an error is + * returned and the entry is not loaded. * - * Description: - * This loads a usage entry saved previously by UpdateUsageEntry. The - * signature at the beginning of the buffer is verified and the buffer will - * be decrypted. Then the verification field in the entry will be verified. - * The index in the entry must match the index passed in. The generation - * number in the entry will be compared against the entry's corresponding - * generation number in the header. If it is off by 1, a warning is returned, - * but the entry is still loaded. This warning may be logged by the CDM - * layer. If the generation number is off by more than 1, an error is - * returned and the entry is not loaded. + * OEMCrypto shall call ODK_ReloadClockValues, as described in "License + * Duration and Renewal" to set the session's clock values. * - * OEMCrypto shall call ODK_ReloadClockValues, as described in "License - * Duration and Renewal" to set the session's clock values. + * If the entry is already loaded into another open session, then this fails + * and returns OEMCrypto_ERROR_INVALID_SESSION. If the session already has a + * usage entry associated with it, the error + * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. * - * If the entry is already loaded into another open session, then this fails - * and returns OEMCrypto_ERROR_INVALID_SESSION. If the session already has a - * usage entry associated with it, the error - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES is returned. + * Before version API 16, the usage entry stored the time that the license + * was loaded. This value is now interpreted as the time that the licence + * request was signed. This can be achieved by simply renaming the field and + * using the same value when reloading an older entry. * - * Before version API 16, the usage entry stored the time that the license - * was loaded. This value is now interpreted as the time that the licence - * request was signed. This can be achieved by simply renaming the field and - * using the same value when reloading an older entry. + * @param[in] session: handle for the session to be used. + * @param[in] usage_entry_number: index of existing usage entry. + * @param[in] buffer: pointer to memory containing encrypted usage table entry. + * @param[in] buffer_length: length of the buffer, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] usage_entry_number: index of existing usage entry. - * [in] buffer: pointer to memory containing encrypted usage table entry. - * [in] buffer_length: length of the buffer, in bytes. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - index beyond end of table. - * OEMCrypto_ERROR_INVALID_SESSION - entry associated with another session or - * the index is wrong. - * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by - * exactly 1. - * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more - * than 1. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. - * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES - if there already is a usage entry - * loaded into this session + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE index beyond end of table. + * @retval OEMCrypto_ERROR_INVALID_SESSION entry associated with another + * session or the index is wrong. + * @retval OEMCrypto_WARNING_GENERATION_SKEW if the generation number is off + * by exactly 1. + * @retval OEMCrypto_ERROR_GENERATION_SKEW if the generation number is off by + * more than 1. + * @retval OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature failed. + * @retval OEMCrypto_ERROR_BAD_MAGIC verification string does not match. + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_MULTIPLE_USAGE_ENTRIES if there already is a usage + * entry loaded into this session * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, @@ -4335,68 +4180,63 @@ OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, const uint8_t* buffer, size_t buffer_length); -/* - * OEMCrypto_UpdateUsageEntry +/** + * Updates the session's usage entry and fills buffers with the encrypted and + * signed entry and usage table header. * - * Description: - * Updates the session's usage entry and fills buffers with the encrypted and - * signed entry and usage table header. + * OEMCrypto shall call ODK_UpdateLastPlaybackTime to update the session's + * clock values, as discussed in the document "License Duration and Renewal". + * The values in the session's clock values structure are copied to the usage + * entry. * - * OEMCrypto shall call ODK_UpdateLastPlaybackTime to update the session's - * clock values, as discussed in the document "License Duration and Renewal". - * The values in the session's clock values structure are copied to the usage - * entry. + * OEMCrypto shall update all time and status values in the entry, and then + * increment the entry's generation number. The corresponding generation + * number in the usage table header is also incremented so that it matches + * the one in the entry. The master generation number in the usage table + * header is incremented and the master generation number is copied to secure + * persistent storage. OEMCrypto will encrypt and sign the entry into the + * entry_buffer, and it will encrypt and sign the usage table header into the + * header_buffer. Some actions, such as the first decrypt and deactivating an + * entry, will also increment the entry's generation number as well as + * changing the entry's status and time fields. The first decryption will + * change the status from Inactive to Active, and it will set the time stamp + * "first decrypt". * - * OEMCrypto shall update all time and status values in the entry, and then - * increment the entry's generation number. The corresponding generation - * number in the usage table header is also incremented so that it matches - * the one in the entry. The master generation number in the usage table - * header is incremented and the master generation number is copied to secure - * persistent storage. OEMCrypto will encrypt and sign the entry into the - * entry_buffer, and it will encrypt and sign the usage table header into the - * header_buffer. Some actions, such as the first decrypt and deactivating an - * entry, will also increment the entry's generation number as well as - * changing the entry's status and time fields. The first decryption will - * change the status from Inactive to Active, and it will set the time stamp - * "first decrypt". + * If the usage entry has the flag ForbidReport set, then the flag is + * cleared. It is the responsibility of the CDM layer to call this function + * and save the usage table before the next call to ReportUsage and before + * the CDM is terminated. Failure to do so will result in generation number + * skew, which will invalidate all of the usage table. * - * If the usage entry has the flag ForbidReport set, then the flag is - * cleared. It is the responsibility of the CDM layer to call this function - * and save the usage table before the next call to ReportUsage and before - * the CDM is terminated. Failure to do so will result in generation number - * skew, which will invalidate all of the usage table. + * If either entry_buffer_length or header_buffer_length is not large enough, + * they are set to the needed size, and return OEMCrypto_ERROR_SHORT_BUFFER. + * In this case, the entry is not updated, ForbidReport is not cleared, + * generation numbers are not incremented, and no other work is done. * - * If either entry_buffer_length or header_buffer_length is not large enough, - * they are set to the needed size, and return OEMCrypto_ERROR_SHORT_BUFFER. - * In this case, the entry is not updated, ForbidReport is not cleared, - * generation numbers are not incremented, and no other work is done. + * @param[in] session: handle for the session to be used. + * @param[out] header_buffer: pointer to memory where encrypted usage table + * header is written. + * @param[in,out] header_buffer_length: (in) length of the header_buffer, in + * bytes. (out) actual length of the header_buffer + * @param[out] entry_buffer: pointer to memory where encrypted usage table entry + * is written. + * @param[in,out] entry_buffer_length: (in) length of the entry_buffer, in + * bytes. (out) actual length of the entry_buffer * - * Parameters: - * [in] session: handle for the session to be used. - * [out] header_buffer: pointer to memory where encrypted usage table header - * is written. - * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. - * (out) actual length of the header_buffer - * [out] entry_buffer: pointer to memory where encrypted usage table entry is - * written. - * [in/out] entry_buffer_length: (in) length of the entry_buffer, in bytes. - * (out) actual length of the entry_buffer - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED some devices do not implement usage * tables. - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_UpdateUsageEntry( @@ -4404,393 +4244,363 @@ OEMCryptoResult OEMCrypto_UpdateUsageEntry( size_t* header_buffer_length, OEMCrypto_SharedMemory* entry_buffer, size_t* entry_buffer_length); -/* - * OEMCrypto_DeactivateUsageEntry +/** + * This deactivates the usage entry associated with the current session. This + * means that the status of the usage entry is changed to InactiveUsed if it + * was Active, or InactiveUnused if it was Unused. This also increments the + * entry's generation number, and the header's master generation number. The + * corresponding generation number in the usage table header is also + * incremented so that it matches the one in the entry. The entry's flag + * ForbidReport will be set. This flag prevents an application from + * generating a report of a deactivated license without first saving the + * entry. * - * Description: - * This deactivates the usage entry associated with the current session. This - * means that the status of the usage entry is changed to InactiveUsed if it - * was Active, or InactiveUnused if it was Unused. This also increments the - * entry's generation number, and the header's master generation number. The - * corresponding generation number in the usage table header is also - * incremented so that it matches the one in the entry. The entry's flag - * ForbidReport will be set. This flag prevents an application from - * generating a report of a deactivated license without first saving the - * entry. + * OEMCrypto shall call ODK_DeactivateUsageEntry to update the session's + * clock values, as discussed in the document "License Duration and Renewal". * - * OEMCrypto shall call ODK_DeactivateUsageEntry to update the session's - * clock values, as discussed in the document "License Duration and Renewal". + * It is allowed to call this function multiple times. If the state is + * already InactiveUsed or InactiveUnused, then this function does not change + * the entry or its state. * - * It is allowed to call this function multiple times. If the state is - * already InactiveUsed or InactiveUnused, then this function does not change - * the entry or its state. + * @param[in] session: handle for the session to be used. + * @param[in] pst: pointer to memory containing Provider Session Token. + * @param[in] pst_length: length of the pst, in bytes. * - * Parameters: - * [in] session: handle for the session to be used. - * [in] pst: pointer to memory containing Provider Session Token. - * [in] pst_length: length of the pst, in bytes. + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INVALID_CONTEXT an entry was not created or loaded, + * or the pst does not match. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_CONTEXT - an entry was not created or loaded, or - * the pst does not match. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support pst sizes of at least 255 bytes. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 16. */ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length); -/* - * OEMCrypto_ReportUsage +/** + * All fields of OEMCrypto_PST_Report are in network byte order. * - * Description: - * All fields of OEMCrypto_PST_Report are in network byte order. + * If the buffer_length is not sufficient to hold a report structure, set + * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. * - * If the buffer_length is not sufficient to hold a report structure, set - * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. + * If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry + * or OEMCrypto_LoadUsageEntry, or if the pst does not match that in the + * entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT. * - * If an entry was not loaded or created with OEMCrypto_CreateNewUsageEntry - * or OEMCrypto_LoadUsageEntry, or if the pst does not match that in the - * entry, return the error OEMCrypto_ERROR_INVALID_CONTEXT. + * If the usage entry's flag ForbidReport is set, indicating the entry has + * not been saved since the entry was deactivated, then the error + * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE is returned and a report is not + * generated. Similarly, if any key in the session has been used since the + * last call to OEMCrypto_UpdateUsageEntry, then the report is not generated, + * and OEMCrypto returns the error OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE. * - * If the usage entry's flag ForbidReport is set, indicating the entry has - * not been saved since the entry was deactivated, then the error - * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE is returned and a report is not - * generated. Similarly, if any key in the session has been used since the - * last call to OEMCrypto_UpdateUsageEntry, then the report is not generated, - * and OEMCrypto returns the error OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE. + * The pst_report is filled out by subtracting the times in the Usage Entry + * from the current time on the secure clock. This design was chosen to avoid + * a requirement to sync the device's secure clock with any external clock. * - * The pst_report is filled out by subtracting the times in the Usage Entry - * from the current time on the secure clock. This design was chosen to avoid - * a requirement to sync the device's secure clock with any external clock. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * Valid values for status are: * - * Valid values for status are: + * - 0 = kUnused -- the keys have not been used to decrypt. + * - 1 = kActive -- the keys have been used, and have not been deactivated. + * - 2 = kInactive - deprecated. Use kInactiveUsed or kInactiveUnused. + * - 3 = kInactiveUsed -- the keys have been marked inactive after being + * active. + * - 4 = kInactiveUnused -- they keys have been marked inactive, but were + * never active. + * The clock_security_level is reported as follows: * - * - 0 = kUnused -- the keys have not been used to decrypt. - * - 1 = kActive -- the keys have been used, and have not been deactivated. - * - 2 = kInactive - deprecated. Use kInactiveUsed or kInactiveUnused. - * - 3 = kInactiveUsed -- the keys have been marked inactive after being - * active. - * - 4 = kInactiveUnused -- they keys have been marked inactive, but were - * never active. - * The clock_security_level is reported as follows: + * - 0 = Insecure Clock - clock just uses system time. + * - 1 = Secure Timer - clock runs from a secure timer which is initialized + * from system time when OEMCrypto becomes active and cannot be modified + * by user software or the user while OEMCrypto is active. A secure + * timer cannot run backwards, even while OEMCrypto is not active. + * - 2 = Secure Clock - Real-time clock set from a secure source that + * cannot be modified by user software regardless of whether OEMCrypto + * is active or inactive. The clock time can only be modified by + * tampering with the security software or hardware. + * - 3 = Hardware Secure Clock - Real-time clock set from a secure source + * that cannot be modified by user software and there are security + * features that prevent the user from modifying the clock in hardware, + * such as a tamper proof battery. + * (See drawing in "Widevine Modular DRM Security Integration Guide") * - * - 0 = Insecure Clock - clock just uses system time. - * - 1 = Secure Timer - clock runs from a secure timer which is initialized - * from system time when OEMCrypto becomes active and cannot be modified - * by user software or the user while OEMCrypto is active. A secure - * timer cannot run backwards, even while OEMCrypto is not active. - * - 2 = Secure Clock - Real-time clock set from a secure source that - * cannot be modified by user software regardless of whether OEMCrypto - * is active or inactive. The clock time can only be modified by - * tampering with the security software or hardware. - * - 3 = Hardware Secure Clock - Real-time clock set from a secure source - * that cannot be modified by user software and there are security - * features that prevent the user from modifying the clock in hardware, - * such as a tamper proof battery. - * (See drawing in "Widevine Modular DRM Security Integration Guide") + * After pst_report has been filled in, the HMAC SHA1 signature is computed + * for the buffer from bytes 20 to the end of the pst field. The signature is + * computed using the mac_key[client] which is stored in the usage table. The + * HMAC SHA1 signature is used to prevent a rogue application from using + * OMECrypto_GenerateSignature to forge a Usage Report. * - * After pst_report has been filled in, the HMAC SHA1 signature is computed - * for the buffer from bytes 20 to the end of the pst field. The signature is - * computed using the mac_key[client] which is stored in the usage table. The - * HMAC SHA1 signature is used to prevent a rogue application from using - * OMECrypto_GenerateSignature to forge a Usage Report. + * Before version 16 of this API, seconds_since_license_received was reported + * instead of seconds_since_license_signed. For any practical bookkeeping + * purposes, these events are essentially at the same time. * - * Before version 16 of this API, seconds_since_license_received was reported - * instead of seconds_since_license_signed. For any practical bookkeeping - * purposes, these events are essentially at the same time. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[in] session: handle for the session to be used. + * @param[in] pst: pointer to memory containing Provider Session Token. + * @param[in] pst_length: length of the pst, in bytes. + * @param[out] buffer: pointer to buffer in which usage report should be + * stored. May be null on the first call in order to find required buffer + * size. + * @param[in,out] buffer_length: (in) length of the report buffer, in bytes. + * (out) actual length of the report * - * Parameters: - * [in] session: handle for the session to be used. - * [in] pst: pointer to memory containing Provider Session Token. - * [in] pst_length: length of the pst, in bytes. - * [out] buffer: pointer to buffer in which usage report should be stored. - * May be null on the first call in order to find required buffer size. - * [in/out] buffer_length: (in) length of the report buffer, in bytes. - * (out) actual length of the report + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if report buffer is not large enough + * to hold the output report. + * @retval OEMCrypto_ERROR_INVALID_SESSION no open session with that id. + * @retval OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE if no call to UpdateUsageEntry + * since last call to Deactivate or since key use. + * @retval OEMCrypto_ERROR_WRONG_PST report asked for wrong pst. + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - if report buffer is not large enough to - * hold the output report. - * OEMCrypto_ERROR_INVALID_SESSION - no open session with that id. - * OEMCrypto_ERROR_INVALID_CONTEXT - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE - if no call to UpdateUsageEntry since - * last call to Deactivate or since key use. - * OEMCrypto_ERROR_WRONG_PST - report asked for wrong pst. - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Buffer Sizes: + * @buffer_size * OEMCrypto shall support pst sizes of at least 255 bytes. * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is * larger than the supported size. * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, uint8_t* buffer, size_t* buffer_length); -/* - * OEMCrypto_MoveEntry +/** + * Moves the entry associated with the current session from one location in + * the usage table header to another. This function is used by the CDM layer + * to defragment the usage table. This does not modify any data in the entry, + * except the index and the generation number. The index in the session's + * usage entry will be changed to new_index. The generation number in + * session's usage entry and in the header for new_index will be increased to + * the master generation number, and then the master generation number is + * incremented. If there was an existing entry at the new location, it will + * be overwritten. It is an error to call this when the entry that was at + * new_index is associated with a currently open session. In this case, the + * error code OEMCrypto_ERROR_ENTRY_IN_USE is returned. It is the CDM layer's + * responsibility to call UpdateUsageEntry after moving an entry. It is an + * error for new_index to be beyond the end of the existing usage table + * header. * - * Description: - * Moves the entry associated with the current session from one location in - * the usage table header to another. This function is used by the CDM layer - * to defragment the usage table. This does not modify any data in the entry, - * except the index and the generation number. The index in the session's - * usage entry will be changed to new_index. The generation number in - * session's usage entry and in the header for new_index will be increased to - * the master generation number, and then the master generation number is - * incremented. If there was an existing entry at the new location, it will - * be overwritten. It is an error to call this when the entry that was at - * new_index is associated with a currently open session. In this case, the - * error code OEMCrypto_ERROR_ENTRY_IN_USE is returned. It is the CDM layer's - * responsibility to call UpdateUsageEntry after moving an entry. It is an - * error for new_index to be beyond the end of the existing usage table - * header. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * @param[in] session: handle for the session to be used. + * @param[in] new_index: new index to be used for the session's usage entry * - * Parameters: - * [in] session: handle for the session to be used. - * [in] new_index: new index to be used for the session's usage entry + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE + * @retval OEMCrypto_ERROR_ENTRY_IN_USE + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * OEMCrypto_ERROR_ENTRY_IN_USE - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 13. */ OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, uint32_t new_index); -/* - * OEMCrypto_ShrinkUsageTableHeader +/** + * This shrinks the usage table and the header. This function is used by the + * CDM layer after it has defragmented the usage table and can delete unused + * entries. It is an error if any open session is associated with an entry + * that will be erased - the error OEMCrypto_ERROR_ENTRY_IN_USE shall be + * returned in this case, and the header shall not be modified. If + * new_entry_count is larger than the current size, then the header is not + * changed and the error OEMCrypto_ERROR_UNKNOWN_FAILURE is returned. If the + * header has not been previously loaded, then an error is returned. + * OEMCrypto will increment the master generation number in the header and + * store the new value in secure persistent storage. Then, OEMCrypto will + * encrypt and sign the header into the provided buffer. The generation + * numbers of all remaining entries will remain unchanged. The next time + * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index + * of new_entry_count. * - * Description: - * This shrinks the usage table and the header. This function is used by the - * CDM layer after it has defragmented the usage table and can delete unused - * entries. It is an error if any open session is associated with an entry - * that will be erased - the error OEMCrypto_ERROR_ENTRY_IN_USE shall be - * returned in this case, and the header shall not be modified. If - * new_entry_count is larger than the current size, then the header is not - * changed and the error OEMCrypto_ERROR_UNKNOWN_FAILURE is returned. If the - * header has not been previously loaded, then an error is returned. - * OEMCrypto will increment the master generation number in the header and - * store the new value in secure persistent storage. Then, OEMCrypto will - * encrypt and sign the header into the provided buffer. The generation - * numbers of all remaining entries will remain unchanged. The next time - * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index - * of new_entry_count. + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * If header_buffer_length is not large enough to hold the new table, it is + * set to the needed value, the generation number is not incremented, and + * OEMCrypto_ERROR_SHORT_BUFFER is returned. * - * If header_buffer_length is not large enough to hold the new table, it is - * set to the needed value, the generation number is not incremented, and - * OEMCrypto_ERROR_SHORT_BUFFER is returned. + * If the header has not been loaded or created, return the error + * OEMCrypto_ERROR_UNKNOWN_FAILURE. * - * If the header has not been loaded or created, return the error - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * @param[in] new_entry_count: number of entries in the to be in the header. + * @param[out] header_buffer: pointer to memory where encrypted usage table + * header is written. + * @param[in,out] header_buffer_length: (in) length of the header_buffer, in + * bytes. (out) actual length of the header_buffer * - * Parameters: - * [in] new_entry_count: number of entries in the to be in the header. - * [out] header_buffer: pointer to memory where encrypted usage table header - * is written. - * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. - * (out) actual length of the header_buffer + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_SHORT_BUFFER + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval OEMCrypto_ERROR_ENTRY_IN_USE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_SHORT_BUFFER - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_ENTRY_IN_USE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Usage Table Function" and will not be called simultaneously * with any other function, as if the CDM holds a write lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 13. */ OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, uint8_t* header_buffer, size_t* header_buffer_length); -/* - * OEMCrypto_RemoveSRM +/// @} + +/// @addtogroup test_verify +/// @{ + +/** + * Delete the current SRM. Any valid SRM, regardless of its version number, + * will be installable after this via OEMCrypto_LoadSRM. * - * Description: - * Delete the current SRM. Any valid SRM, regardless of its version number, - * will be installable after this via OEMCrypto_LoadSRM. + * This function should not be implemented on production devices, and will + * only be used to verify unit tests on a test device. * - * This function should not be implemented on production devices, and will - * only be used to verify unit tests on a test device. + * @retval OEMCrypto_SUCCESS if the SRM file was deleted. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED always on production devices. * - * Parameters: - * none - * - * Returns: - * OEMCrypto_SUCCESS - if the SRM file was deleted. - * OEMCrypto_ERROR_NOT_IMPLEMENTED - always on production devices. - * - * Threading: + * @threading * This is an "Initialization and Termination Function" and will not be * called simultaneously with any other function, as if the CDM holds a write * lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 13. */ OEMCryptoResult OEMCrypto_RemoveSRM(void); -/* - * OEMCrypto_SupportsDecryptHash +/** + * Returns the type of hash function supported for Full Decrypt Path Testing. + * A hash type of OEMCrypto_Hash_Not_Supported = 0 means this feature is not + * supported. OEMCrypto is not required by Google to support this feature, + * but support will greatly improve automated testing. A hash type of + * OEMCrypto_CRC_Clear_Buffer = 1 means the device will be able to compute + * the CRC 32 checksum of the decrypted content in the secure buffer after a + * call to OEMCrypto_DecryptCENC. Google intends to provide test applications + * on some platforms, such as Android, that will automate decryption testing + * using the CRC 32 checksum of all frames in some test content. * - * Description: - * Returns the type of hash function supported for Full Decrypt Path Testing. - * A hash type of OEMCrypto_Hash_Not_Supported = 0 means this feature is not - * supported. OEMCrypto is not required by Google to support this feature, - * but support will greatly improve automated testing. A hash type of - * OEMCrypto_CRC_Clear_Buffer = 1 means the device will be able to compute - * the CRC 32 checksum of the decrypted content in the secure buffer after a - * call to OEMCrypto_DecryptCENC. Google intends to provide test applications - * on some platforms, such as Android, that will automate decryption testing - * using the CRC 32 checksum of all frames in some test content. + * If an SOC vendor cannot support CRC 32 checksums of decrypted output, but + * can support some other hash or checksum, then the function should return + * OEMCrypto_Partner_Defined_Hash = 2 and those partners should modify the + * test application to compute the appropriate hash. An application that + * computes the CRC 32 hashes of test content and builds a hash file in the + * correct format will be provided by Widevine. The source of this + * application will be provided so that partners may modify it to compute + * their own hash format and generate their own hashes. * - * If an SOC vendor cannot support CRC 32 checksums of decrypted output, but - * can support some other hash or checksum, then the function should return - * OEMCrypto_Partner_Defined_Hash = 2 and those partners should modify the - * test application to compute the appropriate hash. An application that - * computes the CRC 32 hashes of test content and builds a hash file in the - * correct format will be provided by Widevine. The source of this - * application will be provided so that partners may modify it to compute - * their own hash format and generate their own hashes. + * @retval OEMCrypto_Hash_Not_Supported = 0; + * @retval OEMCrypto_CRC_Clear_Buffer = 1; + * @retval OEMCrypto_Partner_Defined_Hash = 2; * - * Returns: - * OEMCrypto_Hash_Not_Supported = 0; - * OEMCrypto_CRC_Clear_Buffer = 1; - * OEMCrypto_Partner_Defined_Hash = 2; - * - * Threading: + * @threading * This is a "Property Function" and may be called simultaneously with any * other property function or session function, but not any initialization or * usage table function, as if the CDM holds a read lock on the OEMCrypto * system. * - * Version: + * @version * This method is new in API version 15. */ uint32_t OEMCrypto_SupportsDecryptHash(void); -/* - * OEMCrypto_SetDecryptHash +/** + * Set the hash value for the next frame to be decrypted. This function is + * called before the first subsample is passed to OEMCrypto_DecryptCENC, when + * the subsample_flag has the bit OEMCrypto_FirstSubsample set. The hash is + * over all of the frame or sample: encrypted and clear subsamples + * concatenated together, up to, and including the subsample with the + * subsample_flag having the bit OEMCrypto_LastSubsample set. If hashing the + * output is not supported, then this will return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. If the hash is ill formed or there are + * other error conditions, this returns OEMCrypto_ERROR_UNKNOWN_FAILURE. The + * length of the hash will be at most 128 bytes, and will be 4 bytes (32 + * bits) for the default CRC32 hash. * - * Description: - * Set the hash value for the next frame to be decrypted. This function is - * called before the first subsample is passed to OEMCrypto_DecryptCENC, when - * the subsample_flag has the bit OEMCrypto_FirstSubsample set. The hash is - * over all of the frame or sample: encrypted and clear subsamples - * concatenated together, up to, and including the subsample with the - * subsample_flag having the bit OEMCrypto_LastSubsample set. If hashing the - * output is not supported, then this will return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. If the hash is ill formed or there are - * other error conditions, this returns OEMCrypto_ERROR_UNKNOWN_FAILURE. The - * length of the hash will be at most 128 bytes, and will be 4 bytes (32 - * bits) for the default CRC32 hash. + * This may be called before the first call to SelectKey. In that case, this + * function cannot verify that the key control block allows hash + * verification. The function DecryptCENC should verify that the key control + * bit allows hash verification when it is called. If an attempt is made to + * compute a hash when the selected key does not have the bit + * Allow_Hash_Verification set, then a hash should not be computed, and + * OEMCrypto_GetHashErrorCode should return the error + * OEMCrypto_ERROR_UNKNOWN_FAILURE. * - * This may be called before the first call to SelectKey. In that case, this - * function cannot verify that the key control block allows hash - * verification. The function DecryptCENC should verify that the key control - * bit allows hash verification when it is called. If an attempt is made to - * compute a hash when the selected key does not have the bit - * Allow_Hash_Verification set, then a hash should not be computed, and - * OEMCrypto_GetHashErrorCode should return the error - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * OEMCrypto should compute the hash of the frame and then compare it with + * the correct value. If the values differ, then OEMCrypto should latch in an + * error and save the frame number of the bad hash. It is allowed for + * OEMCrypto to postpone computation of the hash until the frame is + * displayed. This might happen if the actual decryption operation is carried + * out by a later step in the video pipeline, or if you are using a partner + * specified hash of the decoded frame. For this reason, an error state must + * be saved until the call to OEMCrypto_GetHashErrorCode is made. * - * OEMCrypto should compute the hash of the frame and then compare it with - * the correct value. If the values differ, then OEMCrypto should latch in an - * error and save the frame number of the bad hash. It is allowed for - * OEMCrypto to postpone computation of the hash until the frame is - * displayed. This might happen if the actual decryption operation is carried - * out by a later step in the video pipeline, or if you are using a partner - * specified hash of the decoded frame. For this reason, an error state must - * be saved until the call to OEMCrypto_GetHashErrorCode is made. + * @param[in] session: session id for current decrypt operation + * @param[in] frame_number: frame number for the recent DecryptCENC sample. + * @param[in] hash: hash or CRC of previously decrypted frame. + * @param[in] hash_length: length of hash, in bytes. * - * Parameters: - * [in] session: session id for current decrypt operation - * [in] frame_number: frame number for the recent DecryptCENC sample. - * [in] hash: hash or CRC of previously decrypted frame. - * [in] hash_length: length of hash, in bytes. + * @retval OEMCrypto_SUCCESS if the hash was set + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED function not implemented + * @retval OEMCrypto_ERROR_INVALID_SESSION session not open + * @retval OEMCrypto_ERROR_SHORT_BUFFER hash_length too short for supported + * hash type + * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE hash_length too long for supported + * hash type + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE other error + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - if the hash was set - * OEMCrypto_ERROR_NOT_IMPLEMENTED - function not implemented - * OEMCrypto_ERROR_INVALID_SESSION - session not open - * OEMCrypto_ERROR_SHORT_BUFFER - hash_length too short for supported hash - * type - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - hash_length too long for supported hash - * type - * OEMCrypto_ERROR_UNKNOWN_FAILURE - other error - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 15. */ OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, @@ -4798,128 +4608,115 @@ OEMCryptoResult OEMCrypto_SetDecryptHash(OEMCrypto_SESSION session, const uint8_t* hash, size_t hash_length); -/* - * OEMCrypto_GetHashErrorCode +/** + * If the hash set in OEMCrypto_SetDecryptHash did not match the computed + * hash, then an error code was saved internally. This function returns that + * error and the frame number of the bad hash. This will be called + * periodically, but might not be in sync with the decrypt loop. OEMCrypto + * shall not reset the error state to "no error" once any frame has failed + * verification. It should be initialized to "no error" when the session is + * first opened. If there is more than one bad frame, it is the implementer's + * choice if it is more useful to return the number of the first bad frame, + * or the most recent bad frame. * - * Description: - * If the hash set in OEMCrypto_SetDecryptHash did not match the computed - * hash, then an error code was saved internally. This function returns that - * error and the frame number of the bad hash. This will be called - * periodically, but might not be in sync with the decrypt loop. OEMCrypto - * shall not reset the error state to "no error" once any frame has failed - * verification. It should be initialized to "no error" when the session is - * first opened. If there is more than one bad frame, it is the implementer's - * choice if it is more useful to return the number of the first bad frame, - * or the most recent bad frame. + * If the hash could not be computed -- either because the + * Allow_Hash_Verification was not set in the key control block, or because + * there were other issues -- this function should return + * OEMCrypto_ERROR_UNKNOWN_FAILURE. * - * If the hash could not be computed -- either because the - * Allow_Hash_Verification was not set in the key control block, or because - * there were other issues -- this function should return - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * @param[in] session: session id for operation. + * @param[out] failed_frame_number: frame number for sample with incorrect hash. * - * Parameters: - * [in] session: session id for operation. - * [out] failed_frame_number: frame number for sample with incorrect hash. + * @retval OEMCrypto_SUCCESS if all frames have had a correct hash + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_BAD_HASH if any frame had an incorrect hash + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE if the hash could not be computed + * @retval OEMCrypto_ERROR_SESSION_LOST_STATE + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED * - * Returns: - * OEMCrypto_SUCCESS - if all frames have had a correct hash - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_BAD_HASH - if any frame had an incorrect hash - * OEMCrypto_ERROR_UNKNOWN_FAILURE - if the hash could not be computed - * OEMCrypto_ERROR_SESSION_LOST_STATE - * OEMCrypto_ERROR_SYSTEM_INVALIDATED - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 15. */ OEMCryptoResult OEMCrypto_GetHashErrorCode(OEMCrypto_SESSION session, uint32_t* failed_frame_number); -/* - * OEMCrypto_AllocateSecureBuffer +/** + * Allocates a secure buffer and fills out the destination buffer information + * in output_descriptor. The integer secure_fd may also be set to indicate + * the source of the buffer. OEMCrypto may use the secure_fd to help track + * the buffer if it wishes. The unit tests will pass a pointer to the same + * destination buffer description and the same secure_fd to + * OEMCrypto_FreeSecureBuffer when the buffer is to be freed. * - * Description: - * Allocates a secure buffer and fills out the destination buffer information - * in output_descriptor. The integer secure_fd may also be set to indicate - * the source of the buffer. OEMCrypto may use the secure_fd to help track - * the buffer if it wishes. The unit tests will pass a pointer to the same - * destination buffer description and the same secure_fd to - * OEMCrypto_FreeSecureBuffer when the buffer is to be freed. + * This is especially helpful if the hash functions above are supported. This + * will only be used by the OEMCrypto unit tests, so we recommend returning + * OEMCrypto_ERROR_NOT_IMPLEMENTED for production devices if performance is + * an issue. If OEMCrypto_ERROR_NOT_IMPLEMENTED is returned, then secure + * buffer unit tests will be skipped. * - * This is especially helpful if the hash functions above are supported. This - * will only be used by the OEMCrypto unit tests, so we recommend returning - * OEMCrypto_ERROR_NOT_IMPLEMENTED for production devices if performance is - * an issue. If OEMCrypto_ERROR_NOT_IMPLEMENTED is returned, then secure - * buffer unit tests will be skipped. + * @param[in] session: session id for operation. + * @param[in] buffer_size: the requested buffer size. + * @param[out] output_descriptor: the buffer descriptor for the created + * buffer. This will be passed into the OEMCrypto_DecryptCENC function. + * @param[out] secure_fd: a pointer to platform dependent file or buffer + * descriptor. This will be passed to OEMCrypto_FreeSecureBuffer. * - * Parameters: - * [in] session: session id for operation. - * [in] buffer_size: the requested buffer size. - * [out] output_descriptor: the buffer descriptor for the created buffer. - * This will be passed into the OEMCrypto_DecryptCENC function. - * [out] secure_fd: a pointer to platform dependent file or buffer - * descriptor. This will be passed to OEMCrypto_FreeSecureBuffer. + * @retval OEMCrypto_SUCCESS if the buffer was created + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_OUTPUT_TOO_LARGE + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE * - * Returns: - * OEMCrypto_SUCCESS - if the buffer was created - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_OUTPUT_TOO_LARGE - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 16. */ OEMCryptoResult OEMCrypto_AllocateSecureBuffer( OEMCrypto_SESSION session, size_t buffer_size, OEMCrypto_DestBufferDesc* output_descriptor, int* secure_fd); -/* - * OEMCrypto_FreeSecureBuffer +/** + * Frees a secure buffer that had previously been created with + * OEMCrypto_AllocateSecureBuffer. Any return value except OEMCrypto_SUCCESS + * will cause the unit test using secure buffers to fail. * - * Description: - * Frees a secure buffer that had previously been created with - * OEMCrypto_AllocateSecureBuffer. Any return value except OEMCrypto_SUCCESS - * will cause the unit test using secure buffers to fail. + * @param[in] session: session id for operation. + * @param[out] output_descriptor: the buffer descriptor modified by + * OEMCrypto_AllocateSecureBuffer + * @param[in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer * - * Parameters: - * [in] session: session id for operation. - * [out] output_descriptor: the buffer descriptor modified by - * OEMCrypto_AllocateSecureBuffer - * [in] secure_fd: The integer returned by OEMCrypto_AllocateSecureBuffer + * @retval OEMCrypto_SUCCESS if the buffer was freed + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE * - * Returns: - * OEMCrypto_SUCCESS - if the buffer was freed - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * - * Threading: + * @threading * This is a "Session Function" and may be called simultaneously with session * functions for other sessions but not simultaneously with other functions * for this session. It will not be called simultaneously with initialization * or usage table functions. It is as if the CDM holds a write lock for this * session, and a read lock on the OEMCrypto system. * - * Version: + * @version * This method is new in API version 16. */ OEMCryptoResult OEMCrypto_FreeSecureBuffer( OEMCrypto_SESSION session, OEMCrypto_DestBufferDesc* output_descriptor, int secure_fd); +/// @} + /****************************************************************************/ /****************************************************************************/ /* The following functions are deprecated. They are not required for the diff --git a/libwvdrmengine/oemcrypto/include/level3.h b/libwvdrmengine/oemcrypto/include/level3.h index f71584b8..846e7ee6 100644 --- a/libwvdrmengine/oemcrypto/include/level3.h +++ b/libwvdrmengine/oemcrypto/include/level3.h @@ -20,6 +20,7 @@ namespace wvoec3 { +// clang-format off #ifdef DYNAMIC_ADAPTER #define Level3_IsInApp _lcc00 #define Level3_Initialize _lcc01 @@ -91,6 +92,7 @@ namespace wvoec3 { #define Level3_LoadEntitledContentKeys _lcc92 #define Level3_CopyBuffer _lcc93 #else +#define Level3_IsInApp _oecc00 #define Level3_Initialize _oecc01 #define Level3_Terminate _oecc02 #define Level3_InstallKeyboxOrOEMCert _oecc03 @@ -162,6 +164,7 @@ namespace wvoec3 { #endif #define Level3_GetInitializationState _oecl3o01 +// clang-format on extern "C" { diff --git a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h index e8f2ad90..5e54cc6b 100644 --- a/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/libwvdrmengine/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. /********************************************************************* * OEMCryptoCENCCommon.h @@ -20,7 +20,11 @@ extern "C" { #endif +/// @addtogroup common_types +/// @{ + /* clang-format off */ +/** Error and result codes returned by OEMCrypto functions. */ typedef enum OEMCryptoResult { OEMCrypto_SUCCESS = 0, OEMCrypto_ERROR_INIT_FAILED = 1, @@ -94,8 +98,7 @@ typedef enum OEMCryptoResult { } OEMCryptoResult; /* clang-format on */ -/* - * OEMCrypto_Usage_Entry_Status. +/** * Valid values for status in the usage table. */ typedef enum OEMCrypto_Usage_Entry_Status { @@ -106,13 +109,14 @@ typedef enum OEMCrypto_Usage_Entry_Status { kInactiveUnused = 4, } OEMCrypto_Usage_Entry_Status; -/* +/** * OEMCrypto_LicenseType is used in the license message to indicate if the key * objects are for content keys, or for entitlement keys. */ typedef enum OEMCrypto_LicenseType { OEMCrypto_ContentLicense = 0, - OEMCrypto_EntitlementLicense = 1 + OEMCrypto_EntitlementLicense = 1, + OEMCrypto_LicenstType_MaxValue = OEMCrypto_EntitlementLicense, } OEMCrypto_LicenseType; /* Private key type used in the provisioning response. */ @@ -121,9 +125,7 @@ typedef enum OEMCrypto_PrivateKeyType { OEMCrypto_ECC_Private_Key = 1, } OEMCrypto_PrivateKeyType; -/* - * OEMCrypto_Substring - * +/** * Used to indicate a substring of a signed message in OEMCrypto_LoadKeys and * other functions which must verify that a parameter is contained within a * signed message. @@ -133,23 +135,22 @@ typedef struct { size_t length; } OEMCrypto_Substring; -/* - * OEMCrypto_KeyObject +/** * Points to the relevant fields for a content key. The fields are extracted * from the License Response message offered to OEMCrypto_LoadKeys(). Each * field points to one of the components of the key. Key data, key control, * and both IV fields are 128 bits (16 bytes): - * key_id - the unique id of this key. - * key_id_length - the size of key_id. OEMCrypto may assume this is at + * @param key_id: the unique id of this key. + * @param key_id_length: the size of key_id. OEMCrypto may assume this is at * most 16. However, OEMCrypto shall correctly handle key id lengths * from 1 to 16 bytes. - * key_data_iv - the IV for performing AES-128-CBC decryption of the + * @param key_data_iv: the IV for performing AES-128-CBC decryption of the * key_data field. - * key_data - the key data. It is encrypted (AES-128-CBC) with the + * @param key_data - the key data. It is encrypted (AES-128-CBC) with the * session's derived encrypt key and the key_data_iv. - * key_control_iv - the IV for performing AES-128-CBC decryption of the + * @param key_control_iv: the IV for performing AES-128-CBC decryption of the * key_control field. - * key_control - the key control block. It is encrypted (AES-128-CBC) with + * @param key_control: the key control block. It is encrypted (AES-128-CBC) with * the content key from the key_data field. * * The memory for the OEMCrypto_KeyObject fields is allocated and freed @@ -163,8 +164,10 @@ typedef struct { OEMCrypto_Substring key_control; } OEMCrypto_KeyObject; +/// @} + #ifdef __cplusplus } #endif -#endif /* WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ */ +#endif // WIDEVINE_ODK_INCLUDE_OEMCRYPTOCENCCOMMON_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/core_message_deserialize.h b/libwvdrmengine/oemcrypto/odk/include/core_message_deserialize.h index c2967dd2..93cf8002 100644 --- a/libwvdrmengine/oemcrypto/odk/include/core_message_deserialize.h +++ b/libwvdrmengine/oemcrypto/odk/include/core_message_deserialize.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. /********************************************************************* * core_message_deserialize.h @@ -53,7 +53,17 @@ bool CoreProvisioningRequestFromMessage( const std::string& oemcrypto_core_message, ODK_ProvisioningRequest* core_provisioning_request); -} /* namespace deserialize */ -} /* namespace oemcrypto_core_message */ +/** + * Serializer counterpart is not used and is therefore not implemented. + * + * Parameters: + * [in] oemcrypto_core_message + * [out] core_common_request + */ +bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message, + ODK_CommonRequest* core_common_request); -#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ */ +} // namespace deserialize +} // namespace oemcrypto_core_message + +#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_DESERIALIZE_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/core_message_serialize.h b/libwvdrmengine/oemcrypto/odk/include/core_message_serialize.h index ffa43f5b..619a0857 100644 --- a/libwvdrmengine/oemcrypto/odk/include/core_message_serialize.h +++ b/libwvdrmengine/oemcrypto/odk/include/core_message_serialize.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. /********************************************************************* * core_message_serialize.h @@ -62,7 +62,7 @@ bool CreateCoreRenewalResponse(const ODK_RenewalRequest& core_request, bool CreateCoreProvisioningResponse(const ODK_ParsedProvisioning& parsed_prov, const ODK_ProvisioningRequest& core_request, std::string* oemcrypto_core_message); -} /* namespace serialize */ -} /* namespace oemcrypto_core_message */ +} // namespace serialize +} // namespace oemcrypto_core_message -#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ */ +#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/core_message_serialize_proto.h b/libwvdrmengine/oemcrypto/odk/include/core_message_serialize_proto.h index 0f494de8..b9dd34e0 100644 --- a/libwvdrmengine/oemcrypto/odk/include/core_message_serialize_proto.h +++ b/libwvdrmengine/oemcrypto/odk/include/core_message_serialize_proto.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. /********************************************************************* * core_message_serialize_proto.h @@ -23,7 +23,7 @@ namespace oemcrypto_core_message { namespace serialize { -/* @ public create response (serializer) functions accepting proto input */ +// @ public create response (serializer) functions accepting proto input /** * Counterpart (serializer) of ODK_ParseLicense (deserializer) @@ -56,7 +56,7 @@ bool CreateCoreProvisioningResponseFromProto( const ODK_ProvisioningRequest& core_request, std::string* oemcrypto_core_message); -} /* namespace serialize */ -} /* namespace oemcrypto_core_message */ +} // namespace serialize +} // namespace oemcrypto_core_message -#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ */ +#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_SERIALIZE_PROTO_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/core_message_types.h b/libwvdrmengine/oemcrypto/odk/include/core_message_types.h index 488e5d21..0f45f006 100644 --- a/libwvdrmengine/oemcrypto/odk/include/core_message_types.h +++ b/libwvdrmengine/oemcrypto/odk/include/core_message_types.h @@ -1,8 +1,8 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. -/* clang-format off */ +// clang-format off /********************************************************************* * core_message_types.h * @@ -38,6 +38,8 @@ * | | ODK_PrepareCoreRenewalRequest | | CoreRenewalRequestFromMessage | * | +------------------------------------+ +-----------------------------------+ * | | ODK_PrepareCoreProvisioningRequest | | CoreProvisioningRequestFromMessage| + * | +------------------------------------+ +-----------------------------------+ + * | | ODK_PrepareCommonRequest | | CoreCommonRequestFromMessage | * +---+------------------------------------+---+-----------------------------------+ * | d | ODK_ParseLicense | s | CreateCoreLicenseResponse | * | +------------------------------------+ +-----------------------------------+ @@ -47,7 +49,7 @@ * +---+------------------------------------+---+-----------------------------------+ * *********************************************************************/ -/* clang-format on */ +// clang-format on #ifndef WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ #define WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ @@ -57,7 +59,18 @@ namespace oemcrypto_core_message { -/* @ input/output structs */ +// @ input/output structs + +/** + * Output structure for CommonRequestFromMessage + * Input structure for CreateCommonResponse + */ +struct ODK_CommonRequest { + uint16_t api_minor_version; + uint16_t api_major_version; + uint32_t nonce; + uint32_t session_id; +}; /** * Output structure for CoreLicenseRequestFromMessage @@ -94,6 +107,6 @@ struct ODK_ProvisioningRequest { std::string device_id; }; -} /* namespace oemcrypto_core_message */ +} // namespace oemcrypto_core_message -#endif /* WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ */ +#endif // WIDEVINE_ODK_INCLUDE_CORE_MESSAGE_TYPES_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/odk.h b/libwvdrmengine/oemcrypto/odk/include/odk.h index 6bbf36fb..588f185a 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk.h @@ -1,11 +1,9 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. -/********************************************************************* - * odk.h - * - * OEMCrypto v16 Core Message Serialization library +/** + * @mainpage OEMCrypto v16 Core Message Serialization library * * For Widevine Modular DRM, there are six message types between a server and * a client device: license request and response, provisioning request and @@ -34,12 +32,30 @@ * ODK library shall be sanitized by the OEMCrypto implementer to prevent * modification by any process running the REE. * - * See the documents "Widevine Core Message Serialization" and "License - * Duration and Renewal" for a detailed description of the ODK API. You can + * See the documents + *
Widevine Core Message Serialization + * and + * License Duration and Renewal + * for a detailed description of the ODK API. You can * find these documents in the widevine repository as * docs/Widevine_Core_Message_Serialization.pdf and * docs/License_Duration_and_Renewal.pdf * + * @defgroup odk_parser Core Message Parsing and Verification + * Functions that parse core messages and verify they are valid. + * TODO(fredgc): add documentation for parsing functions. + * + * @defgroup odk_packer Core Message Creation + * Functions that create core messages. + * TODO(fredgc): add documentation for packing functions. + * + * @defgroup odk_timer Timer and Clock Functions + * Functions related to enforcing timer and duration restrictions. + * TODO(fredgc): add documentation for timers and clocks. + * + * @defgroup common_types Common Types + * Enumerations and structures that are used by several OEMCrypto and ODK + * functions. *********************************************************************/ #ifndef WIDEVINE_ODK_INCLUDE_ODK_H_ @@ -54,26 +70,24 @@ extern "C" { #endif -/* - * ODK_InitializeSessionValues +/// @addtogroup odk_timer +/// @{ + +/** + * This function initializes the session's data structures. It shall be + * called from OEMCrypto_OpenSession. * - * Description: - * This function initializes the session's data structures. It shall be - * called from OEMCrypto_OpenSession. + * @param[out] timer_limits: the session's timer limits. + * @param[out] clock_values: the session's clock values. + * @param[out] nonce_values: the session's ODK nonce values. + * @param[in] api_major_version: the API version of OEMCrypto. + * @param[in] session_id: the session id of the newly created session. * - * Parameters: - * [out] timer_limits: the session's timer limits. - * [out] clock_values: the session's clock values. - * [out] nonce_values: the session's ODK nonce values. - * [in] api_major_version: the API version of OEMCrypto. - * [in] session_id: the session id of the newly created session. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, @@ -81,73 +95,61 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, uint32_t api_major_version, uint32_t session_id); -/* - * ODK_SetNonceValues +/** + * This function sets the nonce value in the session's nonce structure. It + * shall be called from OEMCrypto_GenerateNonce. * - * Description: - * This function sets the nonce value in the session's nonce structure. It - * shall be called from OEMCrypto_GenerateNonce. + * @param[in,out] nonce_values: the session's nonce data. + * @param[in] nonce: the new nonce that was just generated. * - * Parameters: - * [in/out] nonce_values: the session's nonce data. - * [in] nonce: the new nonce that was just generated. + * @retval true on success * - * Returns: - * true on success - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_SetNonceValues(ODK_NonceValues* nonce_values, uint32_t nonce); -/* - * ODK_InitializeClockValues - * - * Description: - * This function initializes the clock values in the session clock_values - * structure. It shall be called from OEMCrypto_PrepAndSignLicenseRequest. +/** + * This function initializes the clock values in the session clock_values + * structure. It shall be called from OEMCrypto_PrepAndSignLicenseRequest. * * Parameters: - * [in/out] clock_values: the session's clock data. - * [in] system_time_seconds: the current time on OEMCrypto's monotonic clock. + * @param[in,out] clock_values: the session's clock data. + * @param[in] system_time_seconds: the current time on OEMCrypto's monotonic + * clock. * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values, uint64_t system_time_seconds); -/* - * ODK_ReloadClockValues +/** + * This function sets the values in the clock_values structure. It shall be + * called from OEMCrypto_LoadUsageEntry. When a usage entry from a v15 or + * earlier license is loaded, the value time_of_license_loaded shall be used + * in place of time_of_license_signed. * - * Description: - * This function sets the values in the clock_values structure. It shall be - * called from OEMCrypto_LoadUsageEntry. When a usage entry from a v15 or - * earlier license is loaded, the value time_of_license_loaded shall be used - * in place of time_of_license_signed. + * @param[in,out] clock_values: the session's clock data. + * @param[in] time_of_license_signed: the value time_license_received from the + * loaded usage entry. + * @param[in] time_of_first_decrypt: the value time_of_first_decrypt from the + * loaded usage entry. + * @param[in] time_of_last_decrypt: the value time_of_last_decrypt from the + * loaded usage entry. + * @param[in] status: the value status from the loaded usage entry. + * @param[in] system_time_seconds: the current time on OEMCrypto's monotonic + * clock. * - * Parameters: - * [in/out] clock_values: the session's clock data. - * [in] time_of_license_signed: the value time_license_received from the - * loaded usage entry. - * [in] time_of_first_decrypt: the value time_of_first_decrypt from the - * loaded usage entry. - * [in] time_of_last_decrypt: the value time_of_last_decrypt from the loaded - * usage entry. - * [in] status: the value status from the loaded usage entry. - * [in] system_time_seconds: the current time on OEMCrypto's monotonic clock. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values, uint64_t time_of_license_signed, @@ -156,173 +158,154 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values, enum OEMCrypto_Usage_Entry_Status status, uint64_t system_time_seconds); -/* - * ODK_AttemptFirstPlayback +/** + * This updates the clock values, and determines if playback may start based + * on the given system time. It uses the values in clock_values to determine + * if this is the first playback for the license or the first playback for + * just this session. * - * Description: - * This updates the clock values, and determines if playback may start based - * on the given system time. It uses the values in clock_values to determine - * if this is the first playback for the license or the first playback for - * just this session. + * This shall be called from the first call in a session to any of + * OEMCrypto_DecryptCENC or any of the OEMCrypto_Generic* functions. * - * This shall be called from the first call in a session to any of - * OEMCrypto_DecryptCENC or any of the OEMCrypto_Generic* functions. + * If OEMCrypto uses a hardware timer, and this function returns + * ODK_SET_TIMER, then the timer should be set to the value pointed to by + * timer_value. * - * If OEMCrypto uses a hardware timer, and this function returns - * ODK_SET_TIMER, then the timer should be set to the value pointed to by - * timer_value. + * @param[in] system_time_seconds: the current time on OEMCrypto's monotonic + * clock, in seconds. + * @param[in] timer_limits: timer limits specified in the license. + * @param[in,out] clock_values: the sessions clock values. + * @param[out] timer_value: set to the new timer value. Only used if the return + * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a + * hardware timer. * - * Parameters: - * [in] system_time_seconds: the current time on OEMCrypto's monotonic clock, - * in seconds. - * [in] timer_limits: timer limits specified in the license. - * [in/out] clock_values: the sessions clock values. - * [out] timer_value: set to the new timer value. Only used if the return - * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a - * hardware timer. - * - * Returns: - * ODK_SET_TIMER: Success. The timer should be reset to the specified value - * and playback is allowed. - * ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is + * @retval ODK_SET_TIMER: Success. The timer should be reset to the specified + * value and playback is allowed. + * @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is * allowed. - * ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. + * @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_AttemptFirstPlayback(uint64_t system_time_seconds, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t* timer_value); -/* - * ODK_UpdateLastPlaybackTime +/** + * Vendors that do not implement their own timer should call + * ODK_UpdateLastPlaybackTime regularly during playback. This updates the + * clock values, and determines if playback may continue based on the given + * system time. This shall be called from any of OEMCrypto_DecryptCENC or any + * of the OEMCrypto_Generic* functions. * - * Description: - * Vendors that do not implement their own timer should call - * ODK_UpdateLastPlaybackTime regularly during playback. This updates the - * clock values, and determines if playback may continue based on the given - * system time. This shall be called from any of OEMCrypto_DecryptCENC or any - * of the OEMCrypto_Generic* functions. + * All Vendors (i.e. those that do or do not implement their own timer) shall + * call ODK_UpdateLastPlaybackTime from the function + * OEMCrypto_UpdateUsageEntry before updating the usage entry so that the + * clock values are accurate. * - * All Vendors (i.e. those that do or do not implement their own timer) shall - * call ODK_UpdateLastPlaybackTime from the function - * OEMCrypto_UpdateUsageEntry before updating the usage entry so that the - * clock values are accurate. + * @param[in] system_time_seconds: the current time on OEMCrypto's monotonic + * clock, in seconds. + * @param[in] timer_limits: timer limits specified in the license. + * @param[in,out] clock_values: the sessions clock values. * - * Parameters: - * [in] system_time_seconds: the current time on OEMCrypto's monotonic clock, - * in seconds. - * [in] timer_limits: timer limits specified in the license. - * [in/out] clock_values: the sessions clock values. + * @retval OEMCrypto_SUCCESS: Success. Playback is allowed. + * @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. * - * Returns: - * OEMCrypto_SUCCESS: Success. Playback is allowed. - * ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_UpdateLastPlaybackTime(uint64_t system_time_seconds, const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values); -/* - * ODK_DeactivateUsageEntry - * - * Description: - * This function modifies the session's clock values to indicate that the - * license has been deactivated. It shall be called from - * OEMCrypto_DeactivateUsageEntry +/** + * This function modifies the session's clock values to indicate that the + * license has been deactivated. It shall be called from + * OEMCrypto_DeactivateUsageEntry * * Parameters: - * [in/out] clock_values: the sessions clock values. + * @param[in,out] clock_values: the sessions clock values. * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_DeactivateUsageEntry(ODK_ClockValues* clock_values); -/* - * ODK_PrepareCoreLicenseRequest +/// @} + +/// @addtogroup odk_packer +/// @{ + +/** + * Modifies the message to include a core license request at the beginning of + * the message buffer. The values in nonce_values are used to populate the + * message. * - * Description: - * Modifies the message to include a core license request at the beginning of - * the message buffer. The values in nonce_values are used to populate the - * message. + * This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest. * - * This shall be called by OEMCrypto from OEMCrypto_PrepAndSignLicenseRequest. + * NOTE: if the message pointer is null and/or input core_message_size is + * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * core_message_size to the size needed. * - * NOTE: if the message pointer is null and/or input core_message_size is - * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * core_message_size to the size needed. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * the ODK library. + * @param[in] message_length: length of the entire message buffer. + * @param[in,out] core_message_size: length of the core message at the beginning + * of the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * @param[in] nonce_values: pointer to the session's nonce data. * - * Parameters: - * [in/out] message: Pointer to memory for the entire message. Modified by - * the ODK library. - * [in] message_length: length of the entire message buffer. - * [in/out] core_message_size: length of the core message at the beginning of - * the message. (in) size of buffer reserved for the core message, in - * bytes. (out) actual length of the core message, in bytes. - * [in] nonce_values: pointer to the session's nonce data. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small - * OEMCrypto_ERROR_INVALID_CONTEXT - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_PrepareCoreLicenseRequest( uint8_t* message, size_t message_length, size_t* core_message_size, const ODK_NonceValues* nonce_values); -/* - * ODK_PrepareCoreRenewalRequest +/** + * Modifies the message to include a core renewal request at the beginning of + * the message buffer. The values in nonce_values, clock_values and + * system_time_seconds are used to populate the message. The nonce_values + * should match those from the license. * - * Description: - * Modifies the message to include a core renewal request at the beginning of - * the message buffer. The values in nonce_values, clock_values and - * system_time_seconds are used to populate the message. The nonce_values - * should match those from the license. + * This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest. * - * This shall be called by OEMCrypto from OEMCrypto_PrepAndSignRenewalRequest. + * If status in clock_values indicates that a license has not been loaded, + * then this is a license release. The ODK library will change the value of + * nonce_values.api_major_version to 15. This will make + * OEMCrypto_PrepAndSignRenewalRequest sign just the message body, as it does + * for all legacy licenses. * - * If status in clock_values indicates that a license has not been loaded, - * then this is a license release. The ODK library will change the value of - * nonce_values.api_major_version to 15. This will make - * OEMCrypto_PrepAndSignRenewalRequest sign just the message body, as it does - * for all legacy licenses. + * NOTE: if the message pointer is null and/or input core_message_size is + * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * core_message_size to the size needed. * - * NOTE: if the message pointer is null and/or input core_message_size is - * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * core_message_size to the size needed. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * the ODK library. + * @param[in] message_length: length of the entire message buffer. + * @param[in,out] core_message_size: length of the core message at the beginning + * of the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * @param[in,out] nonce_values: pointer to the session's nonce data. + * @param[in,out] clock_values: the session's clock values. + * @param[in] system_time_seconds: the current time on OEMCrypto's clock, in + * seconds. * - * Parameters: - * [in/out] message: Pointer to memory for the entire message. Modified by - * the ODK library. - * [in] message_length: length of the entire message buffer. - * [in/out] core_message_size: length of the core message at the beginning of - * the message. (in) size of buffer reserved for the core message, in - * bytes. (out) actual length of the core message, in bytes. - * [in/out] nonce_values: pointer to the session's nonce data. - * [in/out] clock_values: the session's clock values. - * [in] system_time_seconds: the current time on OEMCrypto's clock, in - * seconds. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small - * OEMCrypto_ERROR_INVALID_CONTEXT - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, size_t message_length, @@ -331,77 +314,72 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, ODK_ClockValues* clock_values, uint64_t system_time_seconds); -/* - * ODK_PrepareCoreProvisioningRequest +/** + * Modifies the message to include a core provisioning request at the + * beginning of the message buffer. The values in nonce_values are used to + * populate the message. * - * Description: - * Modifies the message to include a core provisioning request at the - * beginning of the message buffer. The values in nonce_values are used to - * populate the message. + * This shall be called by OEMCrypto from + * OEMCrypto_PrepAndSignProvisioningRequest. * - * This shall be called by OEMCrypto from - * OEMCrypto_PrepAndSignProvisioningRequest. + * The buffer device_id shall be the same string returned by + * OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and + * stable across reboots and factory resets for an L1 device. * - * The buffer device_id shall be the same string returned by - * OEMCrypto_GetDeviceID. The device ID shall be unique to the device, and - * stable across reboots and factory resets for an L1 device. + * NOTE: if the message pointer is null and/or input core_message_size is + * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output + * core_message_size to the size needed. * - * NOTE: if the message pointer is null and/or input core_message_size is - * zero, this function returns OEMCrypto_ERROR_SHORT_BUFFER and sets output - * core_message_size to the size needed. + * @param[in,out] message: Pointer to memory for the entire message. Modified by + * the ODK library. + * @param[in] message_length: length of the entire message buffer. + * @param[in,out] core_message_size: length of the core message at the beginning + * of the message. (in) size of buffer reserved for the core message, in + * bytes. (out) actual length of the core message, in bytes. + * @param[in] nonce_values: pointer to the session's nonce data. + * @param[in] device_id: For devices with a keybox, this is the device ID from + * the keybox. For devices with an OEM Certificate, this is a device + * unique id string. + * @param[in] device_id_length: length of device_id. The device ID can be at + * most 64 bytes. * - * Parameters: - * [in/out] message: Pointer to memory for the entire message. Modified by - * the ODK library. - * [in] message_length: length of the entire message buffer. - * [in/out] core_message_size: length of the core message at the beginning of - * the message. (in) size of buffer reserved for the core message, in - * bytes. (out) actual length of the core message, in bytes. - * [in] nonce_values: pointer to the session's nonce data. - * [in] device_id: For devices with a keybox, this is the device ID from the - * keybox. For devices with an OEM Certificate, this is a device unique - * id string. - * [in] device_id_length: length of device_id. The device ID can be at most - * 64 bytes. + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_SHORT_BUFFER: core_message_size is too small - * OEMCrypto_ERROR_INVALID_CONTEXT - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_PrepareCoreProvisioningRequest( uint8_t* message, size_t message_length, size_t* core_message_size, const ODK_NonceValues* nonce_values, const uint8_t* device_id, size_t device_id_length); -/* - * ODK_InitializeV15Values +/// @} + +/// @addtogroup odk_timer +/// @{ + +/** + * This function sets all limits in the timer_limits struct to the + * key_duration and initializes the other values. The field + * nonce_values.api_major_version will be set to 15. It shall be called from + * OEMCrypto_LoadKeys when loading a legacy license. * - * Description: - * This function sets all limits in the timer_limits struct to the - * key_duration and initializes the other values. The field - * nonce_values.api_major_version will be set to 15. It shall be called from - * OEMCrypto_LoadKeys when loading a legacy license. + * @param[out] timer_limits: The session's timer limits. + * @param[in,out] clock_values: The session's clock values. + * @param[in,out] nonce_values: The session's ODK nonce values. + * @param[in] key_duration: The duration from the first key's key control + * block. In practice, the key duration is the same for all keys and is + * the same as the license duration. + * @param[in] system_time_seconds: The current time on the system clock, as + * described in the document "License Duration and Renewal". * - * Parameters: - * [out] timer_limits: The session's timer limits. - * [in/out] clock_values: The session's clock values. - * [in/out] nonce_values: The session's ODK nonce values. - * [in] key_duration: The duration from the first key's key control block. In - * practice, the key duration is the same for all keys and is the same - * as the license duration. - * [in] system_time_seconds: The current time on the system clock, as - * described in the document "License Duration and Renewal". + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_INVALID_CONTEXT * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_INVALID_CONTEXT - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, @@ -409,40 +387,35 @@ OEMCryptoResult ODK_InitializeV15Values(ODK_TimerLimits* timer_limits, uint32_t key_duration, uint64_t system_time_seconds); -/* - * ODK_RefreshV15Values +/** + * This function updates the clock_values as needed if a v15 renewal is + * accepted. The field nonce_values.api_major_version is verified to be 15. * - * Description: - * This function updates the clock_values as needed if a v15 renewal is - * accepted. The field nonce_values.api_major_version is verified to be 15. + * This is called from OEMCrypto_RefreshKeys for a valid license renewal. + * OEMCrypto shall pass in the current system time, and the key duration from + * the first object in the OEMCrypto_KeyRefreshObject. * - * This is called from OEMCrypto_RefreshKeys for a valid license renewal. - * OEMCrypto shall pass in the current system time, and the key duration from - * the first object in the OEMCrypto_KeyRefreshObject. + * @param[in] timer_limits: The session's timer limits. + * @param[in,out] clock_values: The session's clock values. + * @param[in] nonce_values: The session's ODK nonce values. + * @param[in] system_time_seconds: The current time on the system clock, as + * described in the document "License Duration and Renewal". + * @param[in] new_key_duration: The duration from the first + * OEMCrypto_KeyRefreshObject in key_array. + * @param[out] timer_value: set to the new timer value. Only used if the return + * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a + * hardware timer. * - * Parameters: - * [in] timer_limits: The session's timer limits. - * [in/out] clock_values: The session's clock values. - * [in] nonce_values: The session's ODK nonce values. - * [in] system_time_seconds: The current time on the system clock, as - * described in the document "License Duration and Renewal". - * [in] new_key_duration: The duration from the first - * OEMCrypto_KeyRefreshObject in key_array. - * [out] timer_value: set to the new timer value. Only used if the return - * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a - * hardware timer. - * - * Returns: - * OEMCrypto_SUCCESS - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * ODK_SET_TIMER: Success. The timer should be reset to the specified value - * and playback is allowed. - * ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is + * @retval OEMCrypto_SUCCESS + * @retval OEMCrypto_ERROR_UNKNOWN_FAILURE + * @retval ODK_SET_TIMER: Success. The timer should be reset to the specified + * value and playback is allowed. + * @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is * allowed. - * ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. + * @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, @@ -451,67 +424,70 @@ OEMCryptoResult ODK_RefreshV15Values(const ODK_TimerLimits* timer_limits, uint32_t new_key_duration, uint64_t* timer_value); -/* - * ODK_ParseLicense +/// @} + +/// @addtogroup odk_parser +/// @{ + +/** + * The function ODK_ParseLicense will parse the message and verify fields in + * the message. * - * Description: - * The function ODK_ParseLicense will parse the message and verify fields in - * the message. + * If the message does not parse correctly, ODK_VerifyAndParseLicense will + * return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM + * layer above. * - * If the message does not parse correctly, ODK_VerifyAndParseLicense will - * return ODK_ERROR_CORE_MESSAGE that OEMCrypto should return to the CDM - * layer above. + * If the API in the message is not 16, then ODK_UNSUPPORTED_API is returned. * - * If the API in the message is not 16, then ODK_UNSUPPORTED_API is returned. + * If initial_license_load is true, and nonce_required in the license is + * true, then the ODK library shall verify that nonce_values->nonce and + * nonce_values->session_id are the same as those in the message. If + * verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE. * - * If initial_license_load is true, and nonce_required in the license is - * true, then the ODK library shall verify that nonce_values->nonce and - * nonce_values->session_id are the same as those in the message. If - * verification fails, then it shall return OEMCrypto_ERROR_INVALID_NONCE. + * If initial_license_load is false, and nonce_required is true, then + * ODK_ParseLicense will set the values in nonce_values from those in the + * message. * - * If initial_license_load is false, and nonce_required is true, then - * ODK_ParseLicense will set the values in nonce_values from those in the - * message. + * The function ODK_ParseLicense will verify that each substring points to a + * location in the message body. The message body is the buffer starting at + * message + core_message_length with size message_length - + * core_message_length. * - * The function ODK_ParseLicense will verify that each substring points to a - * location in the message body. The message body is the buffer starting at - * message + core_message_length with size message_length - - * core_message_length. + * If initial_license_load is true, then ODK_ParseLicense shall verify that + * the parameter request_hash matches request_hash in the parsed license. If + * verification fails, then it shall return ODK_ERROR_CORE_MESSAGE. This was + * computed by OEMCrypto when the license was requested. * - * If initial_license_load is true, then ODK_ParseLicense shall verify that - * the parameter request_hash matches request_hash in the parsed license. If - * verification fails, then it shall return ODK_ERROR_CORE_MESSAGE. This was - * computed by OEMCrypto when the license was requested. + * If usage_entry_present is true, then ODK_ParseLicense shall verify that + * the pst in the license has a nonzero length. * - * If usage_entry_present is true, then ODK_ParseLicense shall verify that - * the pst in the license has a nonzero length. + * @param[in] message: pointer to the message buffer. + * @param[in] message_length: length of the entire message buffer. + * @param[in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * @param[in] initial_license_load: true when called for OEMCrypto_LoadLicense + * and false when called for OEMCrypto_ReloadLicense. + * @param[in] usage_entry_present: true if the session has a new usage entry + * associated with it created via OEMCrypto_CreateNewUsageEntry. + * @param[in] request_hash: the hash of the license request core message. This + * was computed by OEMCrypto when the license request was signed. + * @param[in,out] timer_limits: The session's timer limits. These will be + * updated. + * @param[in,out] clock_values: The session's clock values. These will be + * updated. + * @param[in,out] nonce_values: The session's nonce values. These will be + * updated. + * @param[out] parsed_license: the destination for the data. * - * Parameters: - * [in] message: pointer to the message buffer. - * [in] message_length: length of the entire message buffer. - * [in] core_message_size: length of the core message, at the beginning of - * the message buffer. - * [in] initial_license_load: true when called for OEMCrypto_LoadLicense and - * false when called for OEMCrypto_ReloadLicense. - * [in] usage_entry_present: true if the session has a new usage entry - * associated with it created via OEMCrypto_CreateNewUsageEntry. - * [in] request_hash: the hash of the license request core message. This was - * computed by OEMCrypto when the license request was signed. - * [in/out] timer_limits: The session's timer limits. These will be updated. - * [in/out] clock_values: The session's clock values. These will be updated. - * [in/out] nonce_values: The session's nonce values. These will be updated. - * [out] parsed_license: the destination for the data. + * @retval OEMCrypto_SUCCESS + * @retval ODK_ERROR_CORE_MESSAGE: if the message did not parse correctly, or + * there were other incorrect values. An error should be returned to the + * CDM layer. + * @retval ODK_UNSUPPORTED_API + * @retval OEMCrypto_ERROR_INVALID_NONCE * - * Returns: - * OEMCrypto_SUCCESS - * ODK_ERROR_CORE_MESSAGE: if the message did not parse correctly, or there - * were other incorrect values. An error should be returned to the CDM - * layer. - * ODK_UNSUPPORTED_API - * OEMCrypto_ERROR_INVALID_NONCE - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_ParseLicense( const uint8_t* message, size_t message_length, size_t core_message_length, @@ -520,59 +496,55 @@ OEMCryptoResult ODK_ParseLicense( ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, ODK_NonceValues* nonce_values, ODK_ParsedLicense* parsed_license); -/* - * ODK_ParseRenewal +/** + * The function ODK_ParseRenewal will parse the message and verify its + * contents. If the message does not parse correctly, an error of + * ODK_ERROR_CORE_MESSAGE is returned. * - * Description: - * The function ODK_ParseRenewal will parse the message and verify its - * contents. If the message does not parse correctly, an error of - * ODK_ERROR_CORE_MESSAGE is returned. + * ODK_ParseRenewal shall verify that all fields in nonce_values match those + * in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE. * - * ODK_ParseRenewal shall verify that all fields in nonce_values match those - * in the license. Otherwise it shall return OEMCrypto_ERROR_INVALID_NONCE. + * After parsing the message, this function updates the clock_values based on + * the timer_limits and the current system time. If playback may not + * continue, then ODK_TIMER_EXPIRED is returned. * - * After parsing the message, this function updates the clock_values based on - * the timer_limits and the current system time. If playback may not - * continue, then ODK_TIMER_EXPIRED is returned. + * If playback may continue, a return value of ODK_SET_TIMER or + * ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then + * playback may continue until the timer expires. If the return value is + * ODK_DISABLE_TIMER, then playback time is not limited. * - * If playback may continue, a return value of ODK_SET_TIMER or - * ODK_TIMER_EXPIRED is returned. If the return value is ODK_SET_TIMER, then - * playback may continue until the timer expires. If the return value is - * ODK_DISABLE_TIMER, then playback time is not limited. + * If OEMCrypto uses a hardware timer, and this function returns + * ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to + * by timer_value. * - * If OEMCrypto uses a hardware timer, and this function returns - * ODK_SET_TIMER, then OEMCrypto shall set the timer to the value pointed to - * by timer_value. + * @param[in] message: pointer to the message buffer. + * @param[in] message_length: length of the entire message buffer. + * @param[in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * @param[in] nonce_values: pointer to the session's nonce data. + * @param[in] system_time_seconds: the current time on OEMCrypto's clock, in + * seconds. + * @param[in] timer_limits: timer limits specified in the license. + * @param[in,out] clock_values: the sessions clock values. + * @param[out] timer_value: set to the new timer value. Only used if the return + * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a + * hardware timer. * - * Parameters: - * [in] message: pointer to the message buffer. - * [in] message_length: length of the entire message buffer. - * [in] core_message_size: length of the core message, at the beginning of - * the message buffer. - * [in] nonce_values: pointer to the session's nonce data. - * [in] system_time_seconds: the current time on OEMCrypto's clock, in - * seconds. - * [in] timer_limits: timer limits specified in the license. - * [in/out] clock_values: the sessions clock values. - * [out] timer_value: set to the new timer value. Only used if the return - * value is ODK_SET_TIMER. This must be non-null if OEMCrypto uses a - * hardware timer. - * - * Returns: - * ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there were - * other incorrect values. An error should be returned to the CDM layer. - * ODK_SET_TIMER: Success. The timer should be reset to the specified timer - * value. - * ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is + * @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there + * were other incorrect values. An error should be returned to the CDM + * layer. + * @retval ODK_SET_TIMER: Success. The timer should be reset to the specified + * timer value. + * @retval ODK_DISABLE_TIMER: Success, but disable timer. Unlimited playback is * allowed. - * ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. - * ODK_UNSUPPORTED_API - * ODK_STALE_RENEWAL: This renewal is not the most recently signed. It is - * rejected. - * OEMCrypto_ERROR_INVALID_NONCE + * @retval ODK_TIMER_EXPIRED: Set timer as disabled. Playback is not allowed. + * @retval ODK_UNSUPPORTED_API + * @retval ODK_STALE_RENEWAL: This renewal is not the most recently signed. It + * is rejected. + * @retval OEMCrypto_ERROR_INVALID_NONCE * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, size_t core_message_length, @@ -582,56 +554,54 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, ODK_ClockValues* clock_values, uint64_t* timer_value); -/* - * ODK_ParseProvisioning +/** + * The function ODK_ParseProvisioning will parse the message and verify the + * nonce values match those in the license. * - * Description: - * The function ODK_ParseProvisioning will parse the message and verify the - * nonce values match those in the license. + * If the message does not parse correctly, ODK_ParseProvisioning will return + * an error that OEMCrypto should return to the CDM layer above. * - * If the message does not parse correctly, ODK_ParseProvisioning will return - * an error that OEMCrypto should return to the CDM layer above. + * If the API in the message is larger than 16, then ODK_UNSUPPORTED_API is + * returned. * - * If the API in the message is larger than 16, then ODK_UNSUPPORTED_API is - * returned. + * ODK_ParseProvisioning shall verify that nonce_values->nonce and + * nonce_values->session_id are the same as those in the message. Otherwise + * it shall return OEMCrypto_ERROR_INVALID_NONCE. * - * ODK_ParseProvisioning shall verify that nonce_values->nonce and - * nonce_values->session_id are the same as those in the message. Otherwise - * it shall return OEMCrypto_ERROR_INVALID_NONCE. + * The function ODK_ParseProvisioning will verify that each substring points + * to a location in the message body. The message body is the buffer starting + * at message + core_message_length with size message_length - + * core_message_length. * - * The function ODK_ParseProvisioning will verify that each substring points - * to a location in the message body. The message body is the buffer starting - * at message + core_message_length with size message_length - - * core_message_length. + * @param[in] message: pointer to the message buffer. + * @param[in] message_length: length of the entire message buffer. + * @param[in] core_message_size: length of the core message, at the beginning of + * the message buffer. + * @param[in] nonce_values: pointer to the session's nonce data. + * @param[in] device_id: a pointer to a buffer containing the device ID of the + * device. The ODK function will verify it matches that in the message. + * @param[in] device_id_length: the length of the device ID. + * @param[out] parsed_response: destination for the parse data. * - * Parameters: - * [in] message: pointer to the message buffer. - * [in] message_length: length of the entire message buffer. - * [in] core_message_size: length of the core message, at the beginning of - * the message buffer. - * [in] nonce_values: pointer to the session's nonce data. - * [in] device_id: a pointer to a buffer containing the device ID of the - * device. The ODK function will verify it matches that in the message. - * [in] device_id_length: the length of the device ID. - * [out] parsed_response: destination for the parse data. + * @retval OEMCrypto_SUCCESS + * @retval ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there + * were other incorrect values. An error should be returned to the CDM + * layer. + * @retval ODK_UNSUPPORTED_API + * @retval OEMCrypto_ERROR_INVALID_NONCE * - * Returns: - * OEMCrypto_SUCCESS - * ODK_ERROR_CORE_MESSAGE: the message did not parse correctly, or there were - * other incorrect values. An error should be returned to the CDM layer. - * ODK_UNSUPPORTED_API - * OEMCrypto_ERROR_INVALID_NONCE - * - * Version: - * This method is new in version 16 of the API. + * @version + * This method is new in version 16 of the API. */ OEMCryptoResult ODK_ParseProvisioning( const uint8_t* message, size_t message_length, size_t core_message_length, const ODK_NonceValues* nonce_values, const uint8_t* device_id, size_t device_id_length, ODK_ParsedProvisioning* parsed_response); +/// @} + #ifdef __cplusplus } #endif -#endif /* WIDEVINE_ODK_INCLUDE_ODK_H_ */ +#endif // WIDEVINE_ODK_INCLUDE_ODK_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_attributes.h b/libwvdrmengine/oemcrypto/odk/include/odk_attributes.h new file mode 100644 index 00000000..623a0571 --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/include/odk_attributes.h @@ -0,0 +1,14 @@ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#ifndef WIDEVINE_ODK_INCLUDE_ODK_ATTRIBUTES_H_ +#define WIDEVINE_ODK_INCLUDE_ODK_ATTRIBUTES_H_ + +#if defined(__GNUC__) || defined(__clang__) +#define UNUSED __attribute__((__unused__)) +#else +#define UNUSED +#endif + +#endif // WIDEVINE_ODK_INCLUDE_ODK_ATTRIBUTES_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h index 5c9ff303..bd500d87 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ #define WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ @@ -15,51 +15,51 @@ #define ODK_MINOR_VERSION 3 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v16.3 2020-06-02" +#define ODK_RELEASE_DATE "ODK v16.3 2020-08-18" -/* The lowest version number for an ODK message. */ +/* The lowest version number for an ODK message. */ #define ODK_FIRST_VERSION 16 /* Some useful constants. */ #define ODK_DEVICE_ID_LEN_MAX 64 #define ODK_SHA256_HASH_SIZE 32 -/* - * ODK_TimerLimits Structure +/// @addtogroup odk_timer +/// @{ + +/** + * Timer limits are specified in a license and are used to determine when + * playback is allowed. See the document "License Duration and Renewal" for a + * discussion on the time restrictions that may be placed on a license. The + * fields in this structure are directly related to the fields in the core + * license message. The fields are set when OEMCrypto calls the function + * ODK_ParseLicense or ODK_InitializeV15Values. * - * Description: - * Timer limits are specified in a license and are used to determine when - * playback is allowed. See the document "License Duration and Renewal" for a - * discussion on the time restrictions that may be placed on a license. The - * fields in this structure are directly related to the fields in the core - * license message. The fields are set when OEMCrypto calls the function - * ODK_ParseLicense or ODK_InitializeV15Values. + * @param soft_enforce_rental_duration: A boolean controlling the soft or hard + * enforcement of rental duration. + * @param soft_enforce_playback_duration: A boolean controlling the soft or hard + * enforcement of playback duration. + * @param earliest_playback_start_seconds: The earliest time that the first + * playback is allowed. Measured in seconds since the license request was + * signed. For most use cases, this is zero. + * @param rental_duration_seconds: Window of time for the allowed first + * playback. Measured in seconds since the earliest playback start. If + * soft_enforce_rental_duration is true, this applies only to the first + * playback. If soft_enforce_rental_duration is false, then this + * restricts any playback. A value of zero means no limit. + * @param total_playback_duration_seconds: Window of time for allowed playback. + * Measured in seconds since the first playback start. If + * soft_enforce_playback_duration is true, this applies only to the start + * of playback for any session. If soft_enforce_playback_duration is + * false, then this restricts any playback. A value of zero means no + * limit. + * @param initial_renewal_duration_seconds: Window of time for allowed playback. + * Measured in seconds since the first playback start. This value is only + * used to start the renewal timer. After a renewal message is loaded, + * the timer will be reset. A value of zero means no limit. * - * Fields: - * soft_enforce_rental_duration: A boolean controlling the soft or hard - * enforcement of rental duration. - * soft_enforce_playback_duration: A boolean controlling the soft or hard - * enforcement of playback duration. - * earliest_playback_start_seconds: The earliest time that the first playback - * is allowed. Measured in seconds since the license request was signed. For - * most use cases, this is zero. - * rental_duration_seconds: Window of time for the allowed first playback. - * Measured in seconds since the earliest playback start. If - * soft_enforce_rental_duration is true, this applies only to the first - * playback. If soft_enforce_rental_duration is false, then this restricts - * any playback. A value of zero means no limit. - * total_playback_duration_seconds: Window of time for allowed playback. - * Measured in seconds since the first playback start. If - * soft_enforce_playback_duration is true, this applies only to the start of - * playback for any session. If soft_enforce_playback_duration is false, then - * this restricts any playback. A value of zero means no limit. - * initial_renewal_duration_seconds: Window of time for allowed playback. - * Measured in seconds since the first playback start. This value is only - * used to start the renewal timer. After a renewal message is loaded, the - * timer will be reset. A value of zero means no limit. - * - * Version: - * This struct changed in API version 16.2. + * @version + * This struct changed in API version 16.2. */ typedef struct { bool soft_enforce_rental_duration; @@ -70,47 +70,44 @@ typedef struct { uint64_t initial_renewal_duration_seconds; } ODK_TimerLimits; -/* - * ODK_ClockValues Structure +/** + * Clock values are modified when decryption occurs or when a renewal is + * processed. They are used to track the current status of the license -- + * i.e. has playback started? When does the timer expire? See the section + * "Complete ODK API" of the document "Widevine Core Message Serialization" + * for a complete list of all fields in this structure. Most of these values + * shall be saved with the usage entry. * - * Description: - * Clock values are modified when decryption occurs or when a renewal is - * processed. They are used to track the current status of the license -- - * i.e. has playback started? When does the timer expire? See the section - * "Complete ODK API" of the document "Widevine Core Message Serialization" - * for a complete list of all fields in this structure. Most of these values - * shall be saved with the usage entry. + * All times are in seconds. Most of the fields in this structure are saved + * in the usage entry. This structure should be initialized when a usage + * entry is created or loaded, and should be used to save a usage entry. It + * is updated using the ODK functions listed below. The time values are based + * on OEMCrypto's system clock, as described in the document "License + * Duration and Renewal". * - * All times are in seconds. Most of the fields in this structure are saved - * in the usage entry. This structure should be initialized when a usage - * entry is created or loaded, and should be used to save a usage entry. It - * is updated using the ODK functions listed below. The time values are based - * on OEMCrypto's system clock, as described in the document "License - * Duration and Renewal". + * @param time_of_license_signed: Time that the license request was signed, + * based on OEMCrypto's system clock. This value shall be stored and + * reloaded with usage entry as time_of_license_received. + * @param time_of_first_decrypt: Time of the first decrypt or call select key, + * based on OEMCrypto's system clock. This is 0 if the license has not + * been used to decrypt any data. This value shall be stored and reloaded + * with usage entry. + * @param time_of_last_decrypt: Time of the most recent decrypt call, based on + * OEMCrypto's system clock. This value shall be stored and reloaded with + * usage entry. + * @param time_of_renewal_request: Time of the most recent renewal request, + * based on OEMCrypto's system clock. This is used to verify that a + * renewal is not stale. + * @param time_when_timer_expires: Time that the current timer expires, based on + * OEMCrypto's system clock. If the timer is active, this is used by the + * ODK library to determine if it has expired. + * @param timer_status: Used internally by the ODK library to indicate the + * current timer status. + * @param status: The license or usage entry status. This value shall be stored + * and reloaded with usage entry. * - * Fields: - * time_of_license_signed: Time that the license request was signed, based on - * OEMCrypto's system clock. This value shall be stored and reloaded with - * usage entry as time_of_license_received. - * time_of_first_decrypt: Time of the first decrypt or call select key, based - * on OEMCrypto's system clock. This is 0 if the license has not been used to - * decrypt any data. This value shall be stored and reloaded with usage entry. - * time_of_last_decrypt: Time of the most recent decrypt call, based on - * OEMCrypto's system clock. This value shall be stored and reloaded with - * usage entry. - * time_of_renewal_request: Time of the most recent renewal request, based on - * OEMCrypto's system clock. This is used to verify that a renewal is not - * stale. - * time_when_timer_expires: Time that the current timer expires, based on - * OEMCrypto's system clock. If the timer is active, this is used by the ODK - * library to determine if it has expired. - * timer_status: Used internally by the ODK library to indicate the current - * timer status. - * status: The license or usage entry status. This value shall be stored and - * reloaded with usage entry. - * - * Version: - * This struct changed in API version 16.2. + * @version + * This struct changed in API version 16.2. */ typedef struct { uint64_t time_of_license_signed; @@ -122,34 +119,30 @@ typedef struct { enum OEMCrypto_Usage_Entry_Status status; } ODK_ClockValues; -/* - * ODK_NonceValues Structure +/** + * Nonce values are used to match a license or provisioning request to a + * license or provisioning response. They are also used to match a renewal + * request and response to a license. For this reason, the api_version might + * be lower than that supported by OEMCrypto. The api_version matches the + * version of the license. Similarly the nonce and session_id match the + * session that generated the license request. For an offline license, these + * might not match the session that is loading the license. We use the nonce + * to prevent a license from being replayed. By also including a session_id + * in the license request and license response, we prevent an attack using + * the birthday paradox to generate nonce collisions on a single device. * - * Description: - * Nonce values are used to match a license or provisioning request to a - * license or provisioning response. They are also used to match a renewal - * request and response to a license. For this reason, the api_version might - * be lower than that supported by OEMCrypto. The api_version matches the - * version of the license. Similarly the nonce and session_id match the - * session that generated the license request. For an offline license, these - * might not match the session that is loading the license. We use the nonce - * to prevent a license from being replayed. By also including a session_id - * in the license request and license response, we prevent an attack using - * the birthday paradox to generate nonce collisions on a single device. + * @param api_major_version: the API version of the license. This is initialized + * to the API version of the ODK library, but may be lower. + * @param api_minor_version: the minor version of the ODK library. This is used + * by the server to verify that device is not using an obsolete version + * of the ODK library. + * @param nonce: a randomly generated number used to prevent replay attacks. + * @param session_id: the session id of the session which signed the license or + * provisioning request. It is used to prevent replay attacks from one + * session to another. * - * Fields: - * api_major_version: the API version of the license. This is initialized to - * the API version of the ODK library, but may be lower. - * api_minor_version: the minor version of the ODK library. This is used by - * the server to verify that device is not using an obsolete version of the - * ODK library. - * nonce: a randomly generated number used to prevent replay attacks. - * session_id: the session id of the session which signed the license or - * provisioning request. It is used to prevent replay attacks from one - * session to another. - * - * Version: - * This struct changed in API version 16.2. + * @version + * This struct changed in API version 16.2. */ typedef struct { uint16_t api_minor_version; @@ -158,29 +151,31 @@ typedef struct { uint32_t session_id; } ODK_NonceValues; -/* - * ODK_ParsedLicense Structure +/// @} + +/// @addtogroup odk_parser +/// @{ + +/** + * The parsed license structure contains information from the license + * message. The function ODK_ParseLicense will fill in the fields of this + * message. All substrings are contained within the message body. * - * Description: - * The parsed license structure contains information from the license - * message. The function ODK_ParseLicense will fill in the fields of this - * message. All substrings are contained within the message body. + * @param enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. + * @param enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is + * 512 bits. + * @param pst: the Provider Session Token. + * @param srm_restriction_data: optional data specifying the minimum SRM + * version. + * @param license_type: specifies if the license contains content keys or + * entitlement keys. + * @param nonce_required: indicates if the license requires a nonce. + * @param timer_limits: time limits of the for the license. + * @param key_array_length: number of keys present. + * @param key_array: set of keys to be installed. * - * Fields: - * enc_mac_keys_iv: IV for decrypting new mac_key. Size is 128 bits. - * enc_mac_keys: encrypted mac_keys for generating new mac_keys. Size is 512 - * bits. - * pst: the Provider Session Token. - * srm_restriction_data: optional data specifying the minimum SRM version. - * license_type: specifies if the license contains content keys or - * entitlement keys. - * nonce_required: indicates if the license requires a nonce. - * timer_limits: time limits of the for the license. - * key_array_length: number of keys present. - * key_array: set of keys to be installed. - * - * Version: - * This struct changed in API version 16.2. + * @version + * This struct changed in API version 16.2. */ typedef struct { OEMCrypto_Substring enc_mac_keys_iv; @@ -194,22 +189,19 @@ typedef struct { OEMCrypto_KeyObject key_array[ODK_MAX_NUM_KEYS]; } ODK_ParsedLicense; -/* - * ODK_ParsedProvisioning Structure +/** + * The parsed provisioning structure contains information from the license + * message. The function ODK_ParseProvisioning will fill in the fields of + * this message. All substrings are contained within the message body. * - * Description: - * The parsed provisioning structure contains information from the license - * message. The function ODK_ParseProvisioning will fill in the fields of - * this message. All substrings are contained within the message body. + * @param key_type: indicates if this key is an RSA or ECC private key. + * @param enc_private_key: encrypted private key for the DRM certificate. + * @param enc_private_key_iv: IV for decrypting new private key. Size is 128 + * bits. + * @param encrypted_message_key: used for provisioning 3.0 to derive keys. * - * Fields: - * key_type: indicates if this key is an RSA or ECC private key. - * enc_private_key: encrypted private key for the DRM certificate. - * enc_private_key_iv: IV for decrypting new private key. Size is 128 bits. - * encrypted_message_key: used for provisioning 3.0 to derive keys. - * - * Version: - * This struct changed in API version 16.2. + * @version + * This struct changed in API version 16.2. */ typedef struct { OEMCrypto_PrivateKeyType key_type; @@ -218,4 +210,6 @@ typedef struct { OEMCrypto_Substring encrypted_message_key; /* Used for Prov 3.0 */ } ODK_ParsedProvisioning; -#endif /* WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ */ +/// @} + +#endif // WIDEVINE_ODK_INCLUDE_ODK_STRUCTS_H_ diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_target.h b/libwvdrmengine/oemcrypto/odk/include/odk_target.h index 92252103..cb2774fb 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_target.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_target.h @@ -1,13 +1,13 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file is distributed */ -/* under the Widevine Master License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file is distributed +// under the Widevine Master License Agreement. -/* Partners are expected to edit this file to support target specific code */ -/* and limits. */ +// Partners are expected to edit this file to support target specific code +// and limits. #ifndef WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ #define WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ -/* Maximum number of keys can be modified to suit target's resource tier. */ +// Maximum number of keys can be modified to suit target's resource tier. #define ODK_MAX_NUM_KEYS 32 -#endif /* WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ */ +#endif // WIDEVINE_ODK_INCLUDE_ODK_TARGET_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp index 1cef5ee7..47d4478c 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp @@ -39,8 +39,8 @@ bool ParseRequest(uint32_t message_type, reinterpret_cast(oemcrypto_core_message.c_str()); const size_t buf_length = oemcrypto_core_message.size(); - Message* msg = nullptr; - AllocateMessage(&msg, message_block); + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = reinterpret_cast(blk); InitMessage(msg, const_cast(buf), buf_length); SetSize(msg, buf_length); @@ -70,7 +70,8 @@ bool ParseRequest(uint32_t message_type, // For v16, a release and a renewal use the same message structure. // However, for future API versions, the release might be a separate // message. Otherwise, we expect an exact match of message types. - if (core_message.message_type != message_type && + if (message_type != ODK_Common_Request_Type && + core_message.message_type != message_type && !(message_type == ODK_Renewal_Request_Type && core_message.message_type == ODK_Release_Request_Type)) { return false; @@ -129,5 +130,13 @@ bool CoreProvisioningRequestFromMessage( return true; } +bool CoreCommonRequestFromMessage(const std::string& oemcrypto_core_message, + ODK_CommonRequest* common_request) { + const auto unpacker = Unpack_ODK_PreparedCommonRequest; + ODK_PreparedCommonRequest prepared_common = {}; + return ParseRequest(ODK_Common_Request_Type, oemcrypto_core_message, + common_request, &prepared_common, unpacker); +} + } // namespace deserialize } // namespace oemcrypto_core_message diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp index 45464607..0e05330a 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp @@ -50,8 +50,8 @@ bool CreateResponse(uint32_t message_type, const S& core_request, static constexpr size_t BUF_CAPACITY = 2048; std::vector buf(BUF_CAPACITY, 0); - Message* msg = nullptr; - AllocateMessage(&msg, message_block); + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = reinterpret_cast(blk); InitMessage(msg, buf.data(), buf.capacity()); packer(msg, &response); if (!ValidMessage(msg)) { diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp index cacba429..4b8da06e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp @@ -90,17 +90,14 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license, parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, k.key()); break; } - case video_widevine::License_KeyContainer::CONTENT: { - any_content = true; - if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) { - return false; - } - uint32_t& n = parsed_lic.key_array_length; - parsed_lic.key_array[n++] = KeyContainerToOecKey(serialized_license, k); - break; - } + case video_widevine::License_KeyContainer::CONTENT: + case video_widevine::License_KeyContainer::OPERATOR_SESSION: case video_widevine::License_KeyContainer::ENTITLEMENT: { - any_entitlement = true; + if (k.type() == video_widevine::License_KeyContainer::ENTITLEMENT) { + any_entitlement = true; + } else { + any_content = true; + } if (parsed_lic.key_array_length >= ODK_MAX_NUM_KEYS) { return false; } diff --git a/libwvdrmengine/oemcrypto/odk/src/odk.c b/libwvdrmengine/oemcrypto/odk/src/odk.c index 42e82d5e..0bb6ab18 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk.c @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #include "odk.h" @@ -19,7 +19,7 @@ static OEMCryptoResult ODK_PrepareRequest( uint8_t* message, size_t message_length, size_t* core_message_length, - uint32_t message_type, const ODK_NonceValues* nonce_values, + ODK_MessageType message_type, const ODK_NonceValues* nonce_values, void* prepared_request_buffer, size_t prepared_request_buffer_length) { if (nonce_values == NULL || core_message_length == NULL || prepared_request_buffer == NULL || @@ -27,8 +27,8 @@ static OEMCryptoResult ODK_PrepareRequest( return ODK_ERROR_CORE_MESSAGE; } - Message* msg = NULL; - AllocateMessage(&msg, message_block); + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = (Message*)blk; InitMessage(msg, message, *core_message_length); /* The core message should be at the beginning of the buffer, and with a @@ -95,18 +95,24 @@ static OEMCryptoResult ODK_PrepareRequest( static OEMCryptoResult ODK_ParseResponse( const uint8_t* message, size_t message_length, size_t core_message_length, - uint32_t message_type, const ODK_NonceValues* nonce_values, + ODK_MessageType message_type, const ODK_NonceValues* nonce_values, void* response_buffer, uint32_t response_buffer_length) { if (message == NULL || response_buffer == NULL || core_message_length > message_length) { return ODK_ERROR_CORE_MESSAGE; } - Message* msg = NULL; - AllocateMessage(&msg, message_block); + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = (Message*)blk; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" /* We initialize the message buffer with a size of the entire message * length. */ + /* TODO(b/164486737): Fix the cast-qual warning */ InitMessage(msg, (uint8_t*)message, message_length); +#pragma GCC diagnostic pop + /* The core message should be at the beginning of the buffer, and with a * shorter length. The core message is the part we are parsing. */ SetSize(msg, core_message_length); @@ -168,7 +174,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( return ODK_ERROR_CORE_MESSAGE; } ODK_PreparedLicenseRequest license_request = { - {0}, + {0, 0, {}}, }; return ODK_PrepareRequest( message, message_length, core_message_length, ODK_License_Request_Type, @@ -197,7 +203,7 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, return OEMCrypto_SUCCESS; } - ODK_PreparedRenewalRequest renewal_request = {{0}, 0}; + ODK_PreparedRenewalRequest renewal_request = {{0, 0, {}}, 0}; /* First, we compute the time this request was made relative to the playback * clock. */ if (clock_values->time_of_first_decrypt == 0) { @@ -231,14 +237,14 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest( return ODK_ERROR_CORE_MESSAGE; } ODK_PreparedProvisioningRequest provisioning_request = { - {0}, + {0, 0, {}}, 0, {0}, }; if (device_id_length > sizeof(provisioning_request.device_id)) { return ODK_ERROR_CORE_MESSAGE; } - provisioning_request.device_id_length = device_id_length; + provisioning_request.device_id_length = (uint32_t)device_id_length; if (device_id) { memcpy(provisioning_request.device_id, device_id, device_id_length); } @@ -261,7 +267,9 @@ OEMCryptoResult ODK_ParseLicense( return ODK_ERROR_CORE_MESSAGE; } - ODK_LicenseResponse license_response = {{{0}}, parsed_license, {0}}; + ODK_LicenseResponse license_response = {{{0, 0, {}}}, NULL, {0}}; + license_response.parsed_license = parsed_license; + const OEMCryptoResult err = ODK_ParseResponse( message, message_length, core_message_length, ODK_License_Response_Type, NULL, &license_response, sizeof(ODK_LicenseResponse)); @@ -342,7 +350,7 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, } ODK_RenewalResponse renewal_response = { - {{0}, 0}, + {{0, 0, {}}, 0}, 0, }; const OEMCryptoResult err = ODK_ParseResponse( @@ -378,8 +386,9 @@ OEMCryptoResult ODK_ParseProvisioning( return ODK_ERROR_CORE_MESSAGE; } - ODK_ProvisioningResponse provisioning_response = {{{0}, 0, {0}}, - parsed_response}; + ODK_ProvisioningResponse provisioning_response = {{{0, 0, {}}, 0, {0}}, NULL}; + provisioning_response.parsed_provisioning = parsed_response; + if (device_id_length > ODK_DEVICE_ID_LEN_MAX) { return ODK_ERROR_CORE_MESSAGE; } diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_assert.h b/libwvdrmengine/oemcrypto/odk/src/odk_assert.h index 6fda98b1..149021fd 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_assert.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_assert.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_SRC_ODK_ASSERT_H_ #define WIDEVINE_ODK_SRC_ODK_ASSERT_H_ @@ -21,4 +21,4 @@ extern "C" { } #endif -#endif /* WIDEVINE_ODK_SRC_ODK_ASSERT_H_ */ +#endif // WIDEVINE_ODK_SRC_ODK_ASSERT_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_endian.h b/libwvdrmengine/oemcrypto/odk/src/odk_endian.h index 2a6f143e..00dc01ec 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_endian.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_endian.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ #define WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ @@ -15,7 +15,7 @@ extern "C" { #define oemcrypto_be32toh be32toh #define oemcrypto_htobe64 htobe64 #define oemcrypto_be64toh be64toh -#else /* defined(__linux__) || defined(__ANDROID__) */ +#else /* defined(__linux__) || defined(__ANDROID__) */ uint32_t oemcrypto_htobe32(uint32_t u32); uint32_t oemcrypto_be32toh(uint32_t u32); uint64_t oemcrypto_htobe64(uint64_t u64); @@ -26,4 +26,4 @@ uint64_t oemcrypto_be64toh(uint64_t u64); } #endif -#endif /* WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ */ +#endif // WIDEVINE_ODK_SRC_ODK_ENDIAN_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c index 76c685f5..a03f0f61 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.c @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #include #include diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h index b1e03ee5..6f3f994e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_overflow.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ #define WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ @@ -20,4 +20,4 @@ int odk_add_overflow_ux(size_t a, size_t b, size_t* c); } #endif -#endif /* WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ */ +#endif // WIDEVINE_ODK_SRC_ODK_OVERFLOW_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c index efd94755..ae53e735 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.c @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. /* * This code is auto-generated, do not edit @@ -193,6 +193,10 @@ void Unpack_ODK_PreparedProvisioningRequest( UnpackArray(msg, &obj->device_id[0], sizeof(obj->device_id)); } +void Unpack_ODK_PreparedCommonRequest(Message* msg, + ODK_PreparedCommonRequest* obj) { + Unpack_ODK_CoreMessage(msg, &obj->core_message); +} /* @@ odk deserialize */ void Unpack_ODK_LicenseResponse(Message* msg, ODK_LicenseResponse* obj) { diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h index f35f1782..73f4abd1 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_serialize.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. /* * This code is auto-generated, do not edit @@ -43,7 +43,10 @@ void Unpack_ODK_PreparedRenewalRequest(Message* msg, void Unpack_ODK_PreparedProvisioningRequest( Message* msg, ODK_PreparedProvisioningRequest* obj); +void Unpack_ODK_PreparedCommonRequest(Message* msg, + ODK_PreparedCommonRequest* obj); + #ifdef __cplusplus -} /* extern "C" */ +} // extern "C" #endif -#endif /* WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ */ +#endif // WIDEVINE_ODK_SRC_ODK_SERIALIZE_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h index 3c5a502b..7b1f6de7 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ #define WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ @@ -22,9 +22,11 @@ typedef enum { ODK_Provisioning_Request_Type = 5, ODK_Provisioning_Response_Type = 6, - /* Reserve future message types to support forward compatibility. */ + // Reserve future message types to support forward compatibility. ODK_Release_Request_Type = 7, ODK_Release_Response_Type = 8, + ODK_Common_Request_Type = 9, + ODK_Common_Response_Type = 10, } ODK_MessageType; typedef struct { @@ -48,6 +50,10 @@ typedef struct { uint8_t device_id[ODK_DEVICE_ID_LEN_MAX]; } ODK_PreparedProvisioningRequest; +typedef struct { + ODK_CoreMessage core_message; +} ODK_PreparedCommonRequest; + typedef struct { ODK_PreparedLicenseRequest request; ODK_ParsedLicense* parsed_license; @@ -64,33 +70,32 @@ typedef struct { ODK_ParsedProvisioning* parsed_provisioning; } ODK_ProvisioningResponse; -/* These are the sum of sizeof of each individual member of the request structs - */ -/* without any padding added by the compiler. Make sure they get updated when */ -/* request structs change. Refer to test suite OdkSizeTest in */ -/* ../test/odk_test.cpp for validations of each of the defined request sizes. */ +// These are the sum of sizeof of each individual member of the request structs +// without any padding added by the compiler. Make sure they get updated when +// request structs change. Refer to test suite OdkSizeTest in +// ../test/odk_test.cpp for validations of each of the defined request sizes. #define ODK_LICENSE_REQUEST_SIZE 20 #define ODK_RENEWAL_REQUEST_SIZE 28 #define ODK_PROVISIONING_REQUEST_SIZE 88 -/* These are the possible timer status values. */ -#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 /* Should not happen. */ -/* When the structure has been initialized, but no license is loaded. */ +// These are the possible timer status values. +#define ODK_CLOCK_TIMER_STATUS_UNDEFINED 0 // Should not happen. +// When the structure has been initialized, but no license is loaded. #define ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED 1 -/* After the license is loaded, before a successful decrypt. */ +// After the license is loaded, before a successful decrypt. #define ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED 2 -/* After the license is loaded, if a renewal has also been loaded. */ +// After the license is loaded, if a renewal has also been loaded. #define ODK_CLOCK_TIMER_STATUS_RENEWAL_LOADED 3 -/* The first decrypt has occurred and the timer is active. */ +// The first decrypt has occurred and the timer is active. #define ODK_CLOCK_TIMER_STATUS_ACTIVE 4 -/* The first decrypt has occurred and the timer is unlimited. */ +// The first decrypt has occurred and the timer is unlimited. #define ODK_CLOCK_TIMER_STATUS_UNLIMITED 5 -/* The timer has transitioned from active to expired. */ +// The timer has transitioned from active to expired. #define ODK_CLOCK_TIMER_STATUS_EXPIRED 6 -/* The license has been marked as inactive. */ +// The license has been marked as inactive. #define ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE 7 -/* A helper function for computing timer limits when a renewal is loaded. */ +// A helper function for computing timer limits when a renewal is loaded. OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits, ODK_ClockValues* clock_values, uint64_t system_time_seconds, @@ -101,4 +106,4 @@ OEMCryptoResult ODK_ComputeRenewalDuration(const ODK_TimerLimits* timer_limits, } #endif -#endif /* WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ */ +#endif // WIDEVINE_ODK_SRC_ODK_STRUCTS_PRIV_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c index 0fbcf18e..67c34700 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c @@ -1,11 +1,12 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #include #include #include "odk.h" +#include "odk_attributes.h" #include "odk_overflow.h" #include "odk_structs_priv.h" @@ -311,7 +312,7 @@ OEMCryptoResult ODK_ReloadClockValues(ODK_ClockValues* clock_values, uint64_t time_of_first_decrypt, uint64_t time_of_last_decrypt, enum OEMCrypto_Usage_Entry_Status status, - uint64_t system_time_seconds) { + uint64_t system_time_seconds UNUSED) { if (clock_values == NULL) { return OEMCrypto_ERROR_INVALID_CONTEXT; } diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_util.c b/libwvdrmengine/oemcrypto/odk/src/odk_util.c index ff85a9c2..682bf8eb 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_util.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_util.c @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #include "odk_util.h" diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_util.h b/libwvdrmengine/oemcrypto/odk/src/odk_util.h index 7de668ee..138791fc 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_util.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_util.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_SRC_ODK_UTIL_H_ #define WIDEVINE_ODK_SRC_ODK_UTIL_H_ @@ -23,6 +23,6 @@ int crypto_memcmp(const void* a, const void* b, size_t len); bool ODK_NonceValuesEqual(const ODK_NonceValues* a, const ODK_NonceValues* b); #ifdef __cplusplus -} /* extern "C" */ +} // extern "C" #endif -#endif /* WIDEVINE_ODK_SRC_ODK_UTIL_H_ */ +#endif // WIDEVINE_ODK_SRC_ODK_UTIL_H_ diff --git a/libwvdrmengine/oemcrypto/odk/src/serialization_base.c b/libwvdrmengine/oemcrypto/odk/src/serialization_base.c index 4681b54e..1b32d055 100644 --- a/libwvdrmengine/oemcrypto/odk/src/serialization_base.c +++ b/libwvdrmengine/oemcrypto/odk/src/serialization_base.c @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #include "serialization_base.h" @@ -9,7 +9,6 @@ #include #include "OEMCryptoCENCCommon.h" -#include "odk_assert.h" #include "odk_overflow.h" struct _Message { @@ -20,13 +19,6 @@ struct _Message { MessageStatus status; }; -/* TODO(b/150776214): this can be removed once AllocateMessage gets cleaned up - */ -/* - * odk_static_assert(SIZE_OF_MESSAGE_STRUCT >= sizeof(struct _Message), - * "SIZE_OF_MESSAGE_STRUCT too small"); - */ - bool ValidMessage(Message* message) { if (message == NULL) { return false; @@ -99,8 +91,8 @@ void PackArray(Message* message, const uint8_t* base, size_t size) { } void Pack_OEMCrypto_Substring(Message* msg, const OEMCrypto_Substring* obj) { - uint32_t offset = obj->offset; - uint32_t length = obj->length; + uint32_t offset = (uint32_t)obj->offset; + uint32_t length = (uint32_t)obj->length; Pack_uint32_t(msg, &offset); Pack_uint32_t(msg, &length); } @@ -198,21 +190,6 @@ void InitMessage(Message* message, uint8_t* buffer, size_t capacity) { message->status = MESSAGE_STATUS_OK; } -/* - * The message structure is in the first sizeof(Memory) bytes - * of the buffer - */ -Message* CreateMessage(uint8_t* buffer, size_t buffer_size) { - if (buffer == NULL || buffer_size < sizeof(Message)) return NULL; - Message* message = (Message*)buffer; - message->base = buffer + sizeof(Message); - message->capacity = buffer_size - sizeof(Message); - message->size = 0; - message->read_offset = 0; - message->status = MESSAGE_STATUS_OK; - return message; -} - /* * Set the message to an empty state */ diff --git a/libwvdrmengine/oemcrypto/odk/src/serialization_base.h b/libwvdrmengine/oemcrypto/odk/src/serialization_base.h index c99f4d13..9e7776bf 100644 --- a/libwvdrmengine/oemcrypto/odk/src/serialization_base.h +++ b/libwvdrmengine/oemcrypto/odk/src/serialization_base.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ #define WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ @@ -61,13 +61,6 @@ void UnpackArray(Message* message, uint8_t* address, size_t size); /* copy out */ void Unpack_OEMCrypto_Substring(Message* msg, OEMCrypto_Substring* obj); -/* - * Create a message from a buffer. The message structure consumes the first - * sizeof(Message) bytes of the buffer. The caller is responsible for ensuring - * that the buffer remains allocated for the lifetime of the message. - */ -Message* CreateMessage(uint8_t* buffer, size_t buffer_size); - /* * Initialize a message structure to reference a separate buffer. The caller * is responsible for ensuring that the buffer remains allocated for the @@ -90,7 +83,7 @@ size_t GetOffset(Message* message); size_t SizeOfMessageStruct(); #ifdef __cplusplus -} /* extern "C" */ +} // extern "C" #endif -#endif /* WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ */ +#endif // WIDEVINE_ODK_SRC_SERIALIZATION_BASE_H_ diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/README.md b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/README.md index 0376aeda..f6df3746 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/README.md +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/README.md @@ -62,6 +62,9 @@ library and need to preload that before OEMCrypto unit tests run * Preload the shared library before running OEMCrypto unit tests ```shell + $ cd oemcrypto/odk/test/fuzzing/corpus + $ mkdir license_request_corpus license_response_corpus renewal_request_corpus renewal_response_corpus provisioning_request_corpus provisioning_response_corpus + $ cd /path/to/cdm/repo $ LD_PRELOAD=out/Default/lib/libodk_corpus_generator.so ./out/Default/oemcrypto_unittests ``` diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c index 5b36d89a..9a3e9853 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator.c @@ -1,8 +1,8 @@ -/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. -/* We must define this macro to get RTLD_NEXT definition from */ +// We must define this macro to get RTLD_NEXT definition from #define _GNU_SOURCE #include @@ -21,7 +21,7 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( message, message_length, core_message_length, nonce_values); char* file_name = GetFileName("license_request_corpus"); - /* License Request format expected by fuzzer - [Core License Request] */ + // License Request format expected by fuzzer - [Core License Request] AppendToFile(file_name, (const char*)message, *core_message_length); free(file_name); return oem_crypto_result; @@ -50,9 +50,8 @@ OEMCryptoResult ODK_ParseLicense( nonce_values, parsed_license); char* file_name = GetFileName("license_response_corpus"); - /* License Response format expected by fuzzer - [ODK_ParseLicense_Args][Core - */ - /* License Response] */ + // License Response format expected by fuzzer - [ODK_ParseLicense_Args][Core + // License Response] AppendToFile(file_name, (const char*)&parse_license_args, sizeof(struct ODK_ParseLicense_Args)); AppendToFile(file_name, (const char*)message, core_message_length); @@ -74,8 +73,8 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, nonce_values, clock_values, system_time_seconds); char* file_name = GetFileName("renewal_request_corpus"); - /* License Request format expected by fuzzer - [ODK_ClockValues][Core */ - /* License Request] */ + // License Request format expected by fuzzer - [ODK_ClockValues][Core + // License Request] AppendToFile(file_name, (const char*)clock_values, sizeof(ODK_ClockValues)); AppendToFile(file_name, (const char*)message, *core_message_size); free(file_name); @@ -103,9 +102,8 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, timer_limits, clock_values, timer_value); char* file_name = GetFileName("renewal_response_corpus"); - /* Renewal Response format expected by fuzzer - [ODK_ParseRenewal_Args][Core - */ - /* Renewal Response] */ + // Renewal Response format expected by fuzzer - [ODK_ParseRenewal_Args][Core + // Renewal Response] AppendToFile(file_name, (const char*)&parse_renewal_args, sizeof(struct ODK_ParseRenewal_Args)); AppendToFile(file_name, (const char*)message, core_message_length); @@ -126,8 +124,8 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest( nonce_values, device_id, device_id_length); char* file_name = GetFileName("provisioning_request_corpus"); - /* Provisioning Request format expected by fuzzer - [Core Provisioning */ - /* Request] */ + // Provisioning Request format expected by fuzzer - [Core Provisioning + // Request] AppendToFile(file_name, (const char*)message, *core_message_length); free(file_name); return oem_crypto_result; @@ -150,8 +148,8 @@ OEMCryptoResult ODK_ParseProvisioning( device_id_length, parsed_response); char* file_name = GetFileName("provisioning_response_corpus"); - /* Provisioning Response format expected by fuzzer - */ - /* [ODK_ParseProvisioning_Args][Core Provisioning Response] */ + // Provisioning Response format expected by fuzzer - + // [ODK_ParseProvisioning_Args][Core Provisioning Response] AppendToFile(file_name, (const char*)&parse_provisioning_args, sizeof(struct ODK_ParseProvisioning_Args)); AppendToFile(file_name, (const char*)message, core_message_length); diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c index fa35dca9..3a720d20 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.c @@ -1,6 +1,6 @@ -/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #include "fuzzing/corpus_generator/odk_corpus_generator_helper.h" void AppendToFile(const char* file_name, const char* message, diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h index 2dc6d51b..c2f164ae 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/corpus_generator/odk_corpus_generator_helper.h @@ -1,6 +1,6 @@ -/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ #define WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ @@ -15,5 +15,4 @@ void AppendToFile(const char* file_name, const char* message, char* GetFileName(const char* directory); -#endif /* WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ \ - */ +#endif // WIDEVINE_ODK_TEST_FUZZING_CORPUS_GENERATOR_ODK_CORPUS_GENERATOR_HELPER_H_ diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp index 3e6f2e05..d87e4647 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.cpp @@ -28,13 +28,13 @@ void ConvertDataToValidBools(ODK_ParsedLicense* t) { &t->timer_limits.soft_enforce_rental_duration); } -void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t) {} +void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t UNUSED) {} -void ConvertDataToValidBools(ODK_ParsedProvisioning* t) {} +void ConvertDataToValidBools(ODK_ParsedProvisioning* t UNUSED) {} OEMCryptoResult odk_serialize_LicenseRequest( - const void* in, uint8_t* out, size_t* size, - const ODK_LicenseRequest& core_license_request, + const void* in UNUSED, uint8_t* out, size_t* size, + const ODK_LicenseRequest& core_license_request UNUSED, const ODK_NonceValues* nonce_values) { return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values); } @@ -50,7 +50,7 @@ OEMCryptoResult odk_serialize_RenewalRequest( } OEMCryptoResult odk_serialize_ProvisioningRequest( - const void* in, uint8_t* out, size_t* size, + const void* in UNUSED, uint8_t* out, size_t* size, const ODK_ProvisioningRequest& core_provisioning, const ODK_NonceValues* nonce_values) { const std::string& device_id = core_provisioning.device_id; @@ -99,8 +99,8 @@ OEMCryptoResult odk_deserialize_RenewalResponse( // odk_kdo method, we call Unpack_ODK_PreparedRenewalRequest private method. // playback_time cannot be captured from publicly exposed API // ODK_ParseRenewal. - Message* msg = nullptr; - AllocateMessage(&msg, message_block); + uint8_t blk[SIZE_OF_MESSAGE_STRUCT]; + Message* msg = reinterpret_cast(blk); InitMessage(msg, const_cast(buf), len); SetSize(msg, len); Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg); diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h index 5672c005..fe90657b 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_helper.h @@ -1,6 +1,6 @@ -/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ #define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ @@ -9,6 +9,7 @@ #include "core_message_serialize.h" #include "fuzzing/odk_fuzz_structs.h" +#include "odk_attributes.h" #include "odk_serialize.h" namespace oemcrypto_core_message { @@ -56,24 +57,23 @@ bool kdo_serialize_ProvisioningResponse( const ODK_ParsedProvisioning& parsed_prov, std::string* oemcrypto_core_message); -/* Idea behind having three different functions is: */ -/* Only ODK_ParseLicense structure had fields which needed additional */ -/* procession. Having a single function with templated parameter T was */ -/* failing during compile time because other two structures doesn't have */ -/* fields that need additional processing. Hence to reduce code redundance and - */ -/* make us of common FuzzerMutateResponse across three response fuzzers, */ -/* three independent functions were defined and renewal and provisioning */ -/* functions would be empty as no additional processing is needed for them. */ +// Idea behind having three different functions is: +// Only ODK_ParseLicense structure had fields which needed additional +// procession. Having a single function with templated parameter T was +// failing during compile time because other two structures doesn't have +// fields that need additional processing. Hence to reduce code redundance and +// make us of common FuzzerMutateResponse across three response fuzzers, +// three independent functions were defined and renewal and provisioning +// functions would be empty as no additional processing is needed for them. void ConvertDataToValidBools(ODK_ParsedLicense* t); void ConvertDataToValidBools(ODK_PreparedRenewalRequest* t); void ConvertDataToValidBools(ODK_ParsedProvisioning* t); -/* Forward-declare the libFuzzer's mutator callback. Mark it weak so that */ -/* the program links successfully even outside of --config=asan-fuzzer */ -/* (apparently the only config in which LLVM uses our custom mutator). */ +// Forward-declare the libFuzzer's mutator callback. Mark it weak so that +// the program links successfully even outside of --config=asan-fuzzer +// (apparently the only config in which LLVM uses our custom mutator). extern "C" size_t LLVMFuzzerMutate(uint8_t* Data, size_t Size, size_t MaxSize) __attribute__((weak)); @@ -85,8 +85,8 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size, const size_t kCoreResponseSize = sizeof(T); const size_t kTotalResponseSize = kArgsSize + kCoreResponseSize; - /* Deserializing data in order to make sure it deserializes properly. */ - /* Input byte array format: [function arguments][data to parse]. */ + // Deserializing data in order to make sure it deserializes properly. + // Input byte array format: [function arguments][data to parse]. std::shared_ptr _args(new A()); A* args = _args.get(); memcpy(args, data, kArgsSize); @@ -97,17 +97,16 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size, OEMCryptoResult result = odk_deserialize_fun(buf, size - kArgsSize, args, &nonce_values, &t); - /* If data doesn't deserialize successfully, We copy random bytes into */ - /* T and serialize using kdo function */ - /* which will create a valid oemcrypto core message using */ - /* nonce and request hash from function args. OEMCrypto core message acts as - */ - /* input to odk_kdo. */ + // If data doesn't deserialize successfully, We copy random bytes into + // T and serialize using kdo function + // which will create a valid oemcrypto core message using + // nonce and request hash from function args. OEMCrypto core message acts as + // input to odk_kdo. if (result != OEMCrypto_SUCCESS) { if (max_size < kTotalResponseSize) { return 0; } - /* Initialize remaining bytes needed in data to zero. */ + // Initialize remaining bytes needed in data to zero. if (size < kTotalResponseSize) { memset(data + size, 0, kTotalResponseSize - size); } @@ -115,24 +114,24 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size, memcpy(&t, buf, kCoreResponseSize); } - /* Ask LLVM to run its usual mutations, hopefully giving us interesting */ - /* inputs. We copy deserialized data into pointer data, run mutations */ - /* and copy back the mutated data to args and t */ + // Ask LLVM to run its usual mutations, hopefully giving us interesting + // inputs. We copy deserialized data into pointer data, run mutations + // and copy back the mutated data to args and t memcpy(data + kArgsSize, &t, kCoreResponseSize); LLVMFuzzerMutate(data, kTotalResponseSize, kTotalResponseSize); memcpy(args, data, kArgsSize); memcpy(&t, data + kArgsSize, kCoreResponseSize); - /* Convert boolean flags in parsed message to valid bytes to */ - /* avoid errors from msan. Only needed for parsed license. */ + // Convert boolean flags in parsed message to valid bytes to + // avoid errors from msan. Only needed for parsed license. ConvertDataToValidBools(&t); - /* Serialize the data after mutation. */ + // Serialize the data after mutation. std::string oemcrypto_core_message; if (!kdo_serialize_fun(args, t, &oemcrypto_core_message)) { return 0; } - /* Copy mutated and serialized oemcrypto_core_message to data */ - /* so that it acts as input to odk_kdo function. */ + // Copy mutated and serialized oemcrypto_core_message to data + // so that it acts as input to odk_kdo function. memcpy(data + kArgsSize, oemcrypto_core_message.data(), oemcrypto_core_message.size()); return kArgsSize + oemcrypto_core_message.size(); @@ -149,9 +148,9 @@ size_t FuzzerMutateResponse(uint8_t* data, size_t size, size_t max_size, */ template void odk_kdo(const F& odk_fun, const G& kdo_fun, const uint8_t* in, - const size_t size, const size_t args_size, uint8_t* out) { + const size_t size, const size_t args_size, uint8_t* out UNUSED) { T t = {}; - /* Input byte array format: [function arguments][data to parse] */ + // Input byte array format: [function arguments][data to parse] if (size < args_size) { return; } @@ -187,8 +186,8 @@ static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in, if (size <= clock_value_size) { return; } - /* Input byte array format: [Clock Values][data to parse]. */ - /* Only Renewal Request expects clock values to be present. */ + // Input byte array format: [Clock Values][data to parse]. + // Only Renewal Request expects clock values to be present. std::string input(reinterpret_cast(in) + clock_value_size, size - clock_value_size); T t = {}; @@ -202,5 +201,5 @@ static void kdo_odk(const F& kdo_fun, const G& odk_fun, const uint8_t* in, return; } } -} /* namespace oemcrypto_core_message */ -#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ */ +} // namespace oemcrypto_core_message +#endif // WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_HELPER_H_ diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h index c88de194..37d1b230 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_fuzz_structs.h @@ -1,6 +1,6 @@ -/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ #define WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ @@ -25,4 +25,4 @@ struct ODK_ParseProvisioning_Args { size_t device_id_length; uint8_t device_id[64]; }; -#endif /* WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ */ +#endif // WIDEVINE_ODK_TEST_FUZZING_ODK_FUZZ_STRUCTS_H_ diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp index 80c8ff39..42472fbd 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_license_response_fuzz_with_mutator.cpp @@ -12,7 +12,8 @@ namespace oemcrypto_core_message { // The custom mutator: Ensure that each input can be deserialized properly // by ODK function after mutation. extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, - size_t max_size, unsigned int seed) { + size_t max_size, + unsigned int seed UNUSED) { const size_t kLicenseResponseArgsSize = sizeof(ODK_ParseLicense_Args); if (size < kLicenseResponseArgsSize) { return 0; diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp index 17787a41..2be4a91b 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_provisioning_response_fuzz_with_mutator.cpp @@ -6,13 +6,15 @@ #include #include "fuzzing/odk_fuzz_helper.h" +#include "odk_attributes.h" namespace oemcrypto_core_message { // The custom mutator: Ensure that each input can be deserialized properly // by ODK function after mutation. extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, - size_t max_size, unsigned int seed) { + size_t max_size, + unsigned int seed UNUSED) { const size_t kProvisioningResponseArgsSize = sizeof(ODK_ParseProvisioning_Args); if (size < kProvisioningResponseArgsSize) { diff --git a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp index 0073c4ee..25fa7b00 100644 --- a/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/fuzzing/odk_renewal_response_fuzz_with_mutator.cpp @@ -6,13 +6,15 @@ #include #include "fuzzing/odk_fuzz_helper.h" +#include "odk_attributes.h" namespace oemcrypto_core_message { // The custom mutator: Ensure that each input can be deserialized properly // by ODK function after mutation. extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size, - size_t max_size, unsigned int seed) { + size_t max_size, + unsigned int seed UNUSED) { const size_t kRenewalResponseArgsSize = sizeof(ODK_ParseRenewal_Args); if (size < kRenewalResponseArgsSize) { return 0; diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_core_message_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_core_message_test.cpp new file mode 100644 index 00000000..c8247592 --- /dev/null +++ b/libwvdrmengine/oemcrypto/odk/test/odk_core_message_test.cpp @@ -0,0 +1,37 @@ +// Copyright 2020 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "OEMCryptoCENCCommon.h" +#include "gtest/gtest.h" +#include "odk.h" +#include "third_party/absl/strings/escaping.h" + +namespace wvodk_test { +TEST(CoreMessageTest, RenwalRequest) { + std::string oem = + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst" + "uvwxyzabcdefghijklmnopqrstuvwxyz"; + const uint8_t* buf = reinterpret_cast(oem.c_str()); + uint8_t* message = const_cast(buf); + size_t message_length = 88; + size_t core_message_length = 88; + uint16_t api_minor_version = 16; + uint16_t api_major_version = 16; + uint32_t nonce = 0; + uint32_t timer_status = 2; + uint64_t time = 10; + enum OEMCrypto_Usage_Entry_Status status = kInactiveUsed; + ODK_NonceValues nonce_values{api_minor_version, api_major_version, nonce}; + ODK_ClockValues clock_values{time, time, time, time, + time, timer_status, status}; + uint64_t system_time_seconds = 100; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreRenewalRequest(message, message_length, + &core_message_length, &nonce_values, + &clock_values, system_time_seconds)); + // All messages have at least a five 4-byte fields. + char* m = reinterpret_cast(message); + VLOG(0) << absl::BytesToHexString(std::string(m, core_message_length)); +} +} // namespace wvodk_test diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp deleted file mode 100644 index 3da6744f..00000000 --- a/libwvdrmengine/oemcrypto/odk/test/odk_fuzz.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary - * source code may only be used and distributed under the Widevine Master - * License Agreement. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "OEMCryptoCENCCommon.h" -#include "core_message_deserialize.h" -#include "core_message_serialize.h" -#include "core_message_types.h" -#include "odk.h" -#include "odk_serialize.h" -#include "odk_structs.h" -#include "odk_structs_priv.h" - -typedef std::function - roundtrip_fun; - -using oemcrypto_core_message::ODK_LicenseRequest; -using oemcrypto_core_message::ODK_ProvisioningRequest; -using oemcrypto_core_message::ODK_RenewalRequest; - -using oemcrypto_core_message::deserialize::CoreLicenseRequestFromMessage; -using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage; -using oemcrypto_core_message::deserialize::CoreRenewalRequestFromMessage; - -using oemcrypto_core_message::serialize::CreateCoreLicenseResponse; -using oemcrypto_core_message::serialize::CreateCoreProvisioningResponse; -using oemcrypto_core_message::serialize::CreateCoreRenewalResponse; - -// @ kdo deserialize; odk serialize -static OEMCryptoResult odk_serialize_LicenseRequest( - const void* in, uint8_t* out, size_t* size, - const ODK_LicenseRequest& core_license_request, - const ODK_NonceValues* nonce_values) { - return ODK_PrepareCoreLicenseRequest(out, SIZE_MAX, size, nonce_values); -} - -static OEMCryptoResult odk_serialize_RenewalRequest( - const void* in, uint8_t* out, size_t* size, - const ODK_RenewalRequest& core_renewal, ODK_NonceValues* nonce_values) { - ODK_ClockValues clock{}; - memcpy(&clock, in, sizeof(ODK_ClockValues)); - uint64_t system_time_seconds = core_renewal.playback_time_seconds; - return ODK_PrepareCoreRenewalRequest(out, SIZE_MAX, size, nonce_values, - &clock, system_time_seconds); -} - -static OEMCryptoResult odk_serialize_ProvisioningRequest( - const void* in, uint8_t* out, size_t* size, - const ODK_ProvisioningRequest& core_provisioning, - const ODK_NonceValues* nonce_values) { - const std::string& device_id = core_provisioning.device_id; - return ODK_PrepareCoreProvisioningRequest( - out, SIZE_MAX, size, nonce_values, - reinterpret_cast(device_id.data()), device_id.size()); -} - -/** - * Template arguments: - * T: kdo deserialize output/odk serialize input structure - * F: kdo deserialize function - * G: odk serialize function - * - * raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes - */ -template -static roundtrip_fun kdo_odk(const F& kdo_fun, const G& odk_fun) { - auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size, - size_t clock_value_size) -> void { - if (size <= clock_value_size) { - return; - } - // Input byte array format: [Clock Values][data to parse] - std::string input(reinterpret_cast(in) + clock_value_size, - size - clock_value_size); - T t = {}; - if (!kdo_fun(input, &t)) { - return; - } - ODK_NonceValues nonce_values = {t.api_minor_version, t.api_major_version, - t.nonce, t.session_id}; - OEMCryptoResult err = odk_fun(in, out, &size, t, &nonce_values); - if (OEMCrypto_SUCCESS != err) { - return; - } - assert(0 == memcmp(in + clock_value_size, out, size)); - }; - return roundtrip; -} - -// @ odk deserialize; kdo serialize -namespace { -struct ODK_ParseLicense_Args { - ODK_NonceValues nonce_values; - uint8_t initial_license_load; - uint8_t usage_entry_present; - uint8_t request_hash[32]; - ODK_TimerLimits timer_limits; - ODK_ClockValues clock_values; -}; -struct ODK_ParseRenewal_Args { - ODK_NonceValues nonce_values; - uint64_t system_time; - ODK_TimerLimits timer_limits; - ODK_ClockValues clock_values; -}; -struct ODK_ParseProvisioning_Args { - ODK_NonceValues nonce_values; - size_t device_id_length; - uint8_t device_id[64]; -}; -} // namespace - -bool convert_byte_to_valid_boolean(const bool* in) { - const int value = *reinterpret_cast(in); - return value != 0; -} - -static OEMCryptoResult odk_deserialize_LicenseResponse( - const uint8_t* message, size_t core_message_length, - ODK_ParseLicense_Args* a, ODK_NonceValues* nonce_values, - ODK_ParsedLicense* parsed_lic) { - return ODK_ParseLicense(message, SIZE_MAX, core_message_length, - static_cast(a->initial_license_load), - static_cast(a->usage_entry_present), - a->request_hash, &a->timer_limits, &a->clock_values, - nonce_values, parsed_lic); -} - -static bool kdo_serialize_LicenseResponse(const ODK_ParseLicense_Args* args, - const ODK_ParsedLicense& parsed_lic, - std::string* oemcrypto_core_message) { - const auto& nonce_values = args->nonce_values; - ODK_LicenseRequest core_request{nonce_values.api_minor_version, - nonce_values.api_major_version, - nonce_values.nonce, nonce_values.session_id}; - std::string core_request_sha_256( - reinterpret_cast(args->request_hash), 32); - return CreateCoreLicenseResponse( - parsed_lic, core_request, core_request_sha_256, oemcrypto_core_message); -} - -static OEMCryptoResult odk_deserialize_RenewalResponse( - const uint8_t* buf, size_t len, ODK_ParseRenewal_Args* a, - ODK_NonceValues* nonce_values, ODK_PreparedRenewalRequest* renewal_msg) { - /* Address Sanitizer doesn't like values other than 0 OR 1 for boolean - * variables. Input from fuzzer can be parsed and any random bytes can be - * assigned to boolean variables. Using the workaround to mitigate sanitizer - * errors in fuzzer code and converting random bytes to 0 OR 1. - * This has no negative security impact*/ - a->timer_limits.soft_enforce_playback_duration = - convert_byte_to_valid_boolean( - &a->timer_limits.soft_enforce_playback_duration); - a->timer_limits.soft_enforce_rental_duration = convert_byte_to_valid_boolean( - &a->timer_limits.soft_enforce_rental_duration); - uint64_t timer_value = 0; - OEMCryptoResult err = - ODK_ParseRenewal(buf, SIZE_MAX, len, nonce_values, a->system_time, - &a->timer_limits, &a->clock_values, &timer_value); - if (OEMCrypto_SUCCESS == err) { - Message* msg = nullptr; - AllocateMessage(&msg, message_block); - InitMessage(msg, const_cast(buf), len); - SetSize(msg, len); - Unpack_ODK_PreparedRenewalRequest(msg, renewal_msg); - assert(ValidMessage(msg)); - } - return err; -} - -static bool kdo_serialize_RenewalResponse( - const ODK_ParseRenewal_Args* args, - const ODK_PreparedRenewalRequest& renewal_msg, - std::string* oemcrypto_core_message) { - const auto& nonce_values = args->nonce_values; - ODK_RenewalRequest core_request{ - nonce_values.api_minor_version, nonce_values.api_major_version, - nonce_values.nonce, nonce_values.session_id, renewal_msg.playback_time}; - return CreateCoreRenewalResponse( - core_request, args->timer_limits.initial_renewal_duration_seconds, - oemcrypto_core_message); -} - -static OEMCryptoResult odk_deserialize_ProvisioningResponse( - const uint8_t* buf, size_t len, ODK_ParseProvisioning_Args* a, - ODK_NonceValues* nonce_values, ODK_ParsedProvisioning* parsed_prov) { - return ODK_ParseProvisioning(buf, SIZE_MAX, len, nonce_values, a->device_id, - a->device_id_length, parsed_prov); -} - -static bool kdo_serialize_ProvisioningResponse( - const ODK_ParseProvisioning_Args* args, - const ODK_ParsedProvisioning& parsed_prov, - std::string* oemcrypto_core_message) { - const auto& nonce_values = args->nonce_values; - if (args->device_id_length > sizeof(args->device_id)) { - return false; - } - ODK_ProvisioningRequest core_request{ - nonce_values.api_minor_version, nonce_values.api_major_version, - nonce_values.nonce, nonce_values.session_id, - std::string(reinterpret_cast(args->device_id), - args->device_id_length)}; - return CreateCoreProvisioningResponse(parsed_prov, core_request, - oemcrypto_core_message); -} - -/** - * Template arguments: - * A: struct holding function arguments - * T: odk deserialize output/kdo serialize input structure - * F: odk deserialize function - * G: kdo serialize function - * - * raw bytes -> F deserialize -> struct T -> G serialize -> raw bytes - */ -template -static roundtrip_fun odk_kdo(const F& odk_fun, const G& kdo_fun) { - auto roundtrip = [&](const uint8_t* in, uint8_t* out, size_t size, - size_t args_size) -> void { - // Input byte array format: [function arguments][data to parse] - if (args_size > size) { - return; - } - T t = {}; - const uint8_t* buf = in + args_size; - std::shared_ptr _args(new A()); - A* args = _args.get(); - memcpy(args, in, args_size); - args->nonce_values.api_major_version = ODK_MAJOR_VERSION; - args->nonce_values.api_minor_version = ODK_MINOR_VERSION; - /* - * Input random bytes from autofuzz are interpreted by this script as - * [function args][data to parse]. Odk deserialize functions - * expect the nonce values in function args to match with those - * in data to parse which is not possible with random bytes. - * We follow two pass approach. - * - * 1st pass - We copy random bytes into struct t and call kdo serialize - * with function args which will create oemcrypto core message using nonce - * from function args. Now we have a valid oemcrypto core message which is - * formed using nonce_values from function args which acts as input bytes - * for 2nd pass - * - * 2nd pass - oemcrypto core message from 1st pass guarantees that - * nonce_values in [function args] and core message match. we call - * odk_deserialize using nonce from function args and oemcrypto core message - * from 1st pass. Then we call kdo function which generates oemcrypto core - * message2, which should be equal to oemcrypto_core_message which was input - * to 2nd pass - */ - // TODO(ellurubharath): Use structure aware fuzzing - // 1st pass - memcpy(&t, buf, sizeof(t)); - std::string oemcrypto_core_message; - if (!kdo_fun(args, t, &oemcrypto_core_message)) { - return; - } - assert(oemcrypto_core_message.size() <= size); - - // 2nd pass - ODK_NonceValues nonce_values = args->nonce_values; - OEMCryptoResult result = - odk_fun(reinterpret_cast(oemcrypto_core_message.data()), - oemcrypto_core_message.size(), args, &nonce_values, &t); - if (result != OEMCrypto_SUCCESS) { - return; - } - std::string oemcrypto_core_message2; - if (!kdo_fun(args, t, &oemcrypto_core_message2)) { - return; - } - assert(oemcrypto_core_message == oemcrypto_core_message2); - }; - return roundtrip; -} - -// @ fuzz raw -> parsed -> raw -static void verify_roundtrip(const uint8_t* in, size_t size, - roundtrip_fun roundtrip, size_t args_size) { - std::vector _out(size); - auto out = _out.data(); - roundtrip(in, out, size, args_size); -} - -// Entry point for fuzzer, data is random bytes program gets from autofuzzer -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - verify_roundtrip(data, size, - kdo_odk(CoreLicenseRequestFromMessage, - odk_serialize_LicenseRequest), - 0); - verify_roundtrip(data, size, - kdo_odk(CoreRenewalRequestFromMessage, - odk_serialize_RenewalRequest), - sizeof(ODK_ClockValues)); - verify_roundtrip( - data, size, - kdo_odk(CoreProvisioningRequestFromMessage, - odk_serialize_ProvisioningRequest), - 0); - verify_roundtrip( - data, size, - odk_kdo( - odk_deserialize_LicenseResponse, kdo_serialize_LicenseResponse), - sizeof(ODK_ParseLicense_Args)); - verify_roundtrip( - data, size, - odk_kdo( - odk_deserialize_RenewalResponse, kdo_serialize_RenewalResponse), - sizeof(ODK_ParseRenewal_Args)); - verify_roundtrip(data, size, - odk_kdo( - odk_deserialize_ProvisioningResponse, - kdo_serialize_ProvisioningResponse), - sizeof(ODK_ParseProvisioning_Args)); - - return 0; -} diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp index b5c85650..b6526df8 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test.cpp @@ -209,8 +209,10 @@ TEST(OdkTest, SerializeFieldsStress) { TEST(OdkTest, NullRequestTest) { size_t core_message_length = 0; - ODK_NonceValues nonce_values{0}; - ODK_ClockValues clock_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); + ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); // Assert that nullptr does not cause a core dump. EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, ODK_PrepareCoreLicenseRequest( @@ -250,10 +252,12 @@ TEST(OdkTest, NullResponseTest) { uint8_t message[message_size] = {0}; size_t core_message_length = message_size; uint8_t request_hash[ODK_SHA256_HASH_SIZE] = {0}; - ODK_TimerLimits timer_limits{0}; + ODK_TimerLimits timer_limits; ODK_ParsedLicense parsed_license; - ODK_NonceValues nonce_values{0}; - ODK_ClockValues clock_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); + ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); // Assert that nullptr does not cause a core dump. EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, @@ -322,7 +326,8 @@ TEST(OdkTest, NullResponseTest) { TEST(OdkTest, PrepareCoreLicenseRequest) { uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0}; size_t core_message_length = sizeof(license_message); - ODK_NonceValues nonce_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); EXPECT_EQ(OEMCrypto_SUCCESS, ODK_PrepareCoreLicenseRequest( license_message, sizeof(license_message), &core_message_length, &nonce_values)); @@ -331,7 +336,8 @@ TEST(OdkTest, PrepareCoreLicenseRequest) { TEST(OdkTest, PrepareCoreLicenseRequestSize) { uint8_t license_message[ODK_LICENSE_REQUEST_SIZE] = {0}; size_t core_message_length = sizeof(license_message); - ODK_NonceValues nonce_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); // message length smaller than core message length size_t core_message_length_invalid = core_message_length + 1; EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, @@ -349,8 +355,10 @@ TEST(OdkTest, PrepareCoreLicenseRequestSize) { TEST(OdkTest, PrepareCoreRenewalRequest) { uint8_t renewal_message[ODK_RENEWAL_REQUEST_SIZE] = {0}; size_t core_message_length = sizeof(renewal_message); - ODK_NonceValues nonce_values{0}; - ODK_ClockValues clock_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); + ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); constexpr uint64_t system_time_seconds = 10; EXPECT_EQ(OEMCrypto_SUCCESS, ODK_PrepareCoreRenewalRequest( @@ -363,7 +371,8 @@ TEST(OdkTest, PrepareCoreRenewalRequestTimer) { size_t core_message_length = sizeof(renewal_message); ODK_NonceValues nonce_values{2, 16, 0, 0}; constexpr uint64_t system_time_seconds = 10; - ODK_ClockValues clock_values_updated{0}; + ODK_ClockValues clock_values_updated; + memset(&clock_values_updated, 0, sizeof(clock_values_updated)); // system time smaller than first decrypt time clock_values_updated.time_of_first_decrypt = system_time_seconds + 1; EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, @@ -383,7 +392,8 @@ TEST(OdkTest, PrepareCoreRenewalRequestTimer) { TEST(OdkTest, PrepareCoreProvisioningRequest) { uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; size_t core_message_length = sizeof(provisioning_message); - ODK_NonceValues nonce_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); uint8_t device_id[ODK_DEVICE_ID_LEN_MAX] = {0}; EXPECT_EQ( OEMCrypto_SUCCESS, @@ -395,7 +405,8 @@ TEST(OdkTest, PrepareCoreProvisioningRequest) { TEST(OdkTest, PrepareCoreProvisioningRequestDeviceId) { uint8_t provisioning_message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; size_t core_message_length = sizeof(provisioning_message); - ODK_NonceValues nonce_values{0}; + ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); uint8_t device_id_invalid[ODK_DEVICE_ID_LEN_MAX + 1] = {0}; EXPECT_EQ(ODK_ERROR_CORE_MESSAGE, ODK_PrepareCoreProvisioningRequest( @@ -423,7 +434,8 @@ TEST(OdkTest, RenewalRequestRoundtrip) { const std::vector extra_fields = { {ODK_UINT64, &playback_time, "playback_time"}, }; - ODK_ClockValues clock_values = {0}; + ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); clock_values.time_of_first_decrypt = playback_start; auto odk_prepare_func = [&](uint8_t* const buf, size_t* size, ODK_NonceValues* nonce_values) { @@ -713,6 +725,17 @@ TEST(OdkSizeTest, ProvisioningRequest) { EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE, core_message_length); } +// Verify the version string contains the right version numbers. +TEST(OdkTest, CheckReleaseVersion) { + // Here are the version numbers. + std::string expected_version = std::to_string(ODK_MAJOR_VERSION) + "." + + std::to_string(ODK_MINOR_VERSION); + // Here is the version string. + EXPECT_NE(std::string(ODK_RELEASE_DATE).find(expected_version), + std::string::npos) + << "Version mismatch in odk_structs.h"; +} + } // namespace } // namespace wvodk_test diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h index ad29e89d..1d90dc07 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h +++ b/libwvdrmengine/oemcrypto/odk/test/odk_test_helper.h @@ -1,6 +1,6 @@ -/* Copyright 2019 Google LLC. All rights reserved. This file and proprietary */ -/* source code may only be used and distributed under the Widevine Master */ -/* License Agreement. */ +// Copyright 2019 Google LLC. All rights reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. #ifndef WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ #define WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ @@ -66,7 +66,7 @@ struct ODK_ProvisioningResponseParams { std::vector extra_fields; }; -/* Default values in core_message for testing */ +// Default values in core_message for testing void ODK_SetDefaultCoreFields(ODK_CoreMessage* core_message, uint32_t message_type); void ODK_SetDefaultLicenseResponseParams(ODK_LicenseResponseParams* params); @@ -77,9 +77,9 @@ void ODK_SetDefaultProvisioningResponseParams( size_t ODK_FieldLength(ODK_FieldType type); size_t ODK_AllocSize(ODK_FieldType type); -/* Copy ODK_Field to buf */ +// Copy ODK_Field to buf OEMCryptoResult ODK_WriteSingleField(uint8_t* buf, const ODK_Field* field); -/* Load buf to ODK_Field */ +// Load buf to ODK_Field OEMCryptoResult ODK_ReadSingleField(const uint8_t* buf, const ODK_Field* field); OEMCryptoResult ODK_DumpSingleField(const uint8_t* buf, const ODK_Field* field); OEMCryptoResult ODK_IterFields(ODK_FieldMode mode, uint8_t* buf, @@ -89,11 +89,11 @@ void ODK_ExpectEqualBuf(const void* s1, const void* s2, size_t n, const std::vector& fields); void ODK_ResetOdkFields(std::vector* fields); -/* Serialize core_message and extra_fields into buf */ +// Serialize core_message and extra_fields into buf void ODK_BuildMessageBuffer(ODK_CoreMessage* core_message, const std::vector& extra_fields, uint8_t** buf, uint32_t* buf_size); -} /* namespace wvodk_test */ +} // namespace wvodk_test -#endif /* WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ */ +#endif // WIDEVINE_ODK_TEST_ODK_TEST_HELPER_H_ diff --git a/libwvdrmengine/oemcrypto/odk/test/odk_timer_test.cpp b/libwvdrmengine/oemcrypto/odk/test/odk_timer_test.cpp index c7f00ce1..15a6f78e 100644 --- a/libwvdrmengine/oemcrypto/odk/test/odk_timer_test.cpp +++ b/libwvdrmengine/oemcrypto/odk/test/odk_timer_test.cpp @@ -35,6 +35,7 @@ TEST(OdkTimerBasicTest, NullTest) { TEST(OdkTimerBasicTest, Init) { // Verify that basic initialization sets all of the fields. ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); uint64_t time = 42; ODK_InitializeClockValues(&clock_values, time); EXPECT_EQ(clock_values.time_of_license_signed, time); @@ -50,6 +51,7 @@ TEST(OdkTimerBasicTest, Reload) { // Verify that reloading clock values uses the same values // for fields that can be saved, and sets others to 0. ODK_ClockValues clock_values; + memset(&clock_values, 0, sizeof(clock_values)); uint64_t time = 42u; uint64_t lic_signed = 1u; uint64_t first_decrypt = 2u; @@ -1223,6 +1225,7 @@ TEST_F(ODKUseCase_LimitedDurationLicense, Case5) { TEST_F(RenewalTest, V15Test) { const uint32_t key_duration = 25; ODK_NonceValues nonce_values; + memset(&nonce_values, 0, sizeof(nonce_values)); const uint64_t license_loaded = GetSystemTime(10); EXPECT_EQ( ODK_InitializeV15Values(&timer_limits_, &clock_values_, &nonce_values, diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp index c1b7941a..596ff72a 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.cpp @@ -23,6 +23,14 @@ #include "oemcrypto_rsa_key_shared.h" #include "string_conversions.h" +namespace { + +// Lower bits in SessionId are actual session id. The rest higher bits are +// session type. +const uint32_t kSessionIdTypeShift = 28; +const uint32_t kSessionIdMask = (1u << kSessionIdTypeShift) - 1u; +} // namespace + namespace wvoec_ref { // Note: The class CryptoEngine is configured at compile time by compiling in @@ -64,6 +72,12 @@ SessionId CryptoEngine::OpenSession() { std::unique_lock lock(session_table_lock_); static OEMCrypto_SESSION unique_id = 1; SessionId id = ++unique_id; + // Check if too many sessions have been opened. + if (SessionTypeBits(id) != 0) { + return 0; + } + // Apply session type to higher bits. + id = (kSessionTypeOEMCrypto << kSessionIdTypeShift) | (id & kSessionIdMask); sessions_[id] = MakeSession(id); return id; } @@ -251,4 +265,8 @@ OEMCryptoResult CryptoEngine::SetDestination( return OEMCrypto_SUCCESS; } +uint32_t CryptoEngine::SessionTypeBits(SessionId sid) { + return sid >> kSessionIdTypeShift; +} + } // namespace wvoec_ref diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h index 552f59a7..299c527b 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_engine_ref.h @@ -22,8 +22,8 @@ #include "oemcrypto_key_ref.h" #include "oemcrypto_rsa_key_shared.h" #include "oemcrypto_session.h" -#include "oemcrypto_usage_table_ref.h" #include "oemcrypto_types.h" +#include "oemcrypto_usage_table_ref.h" namespace wvoec_ref { @@ -41,10 +41,16 @@ typedef struct { uint8_t padding[16 - (2 * sizeof(time_t)) % 16]; } TimeInfo; +// Session types are higher (32 - kSessionIdTypeShift) bits in SessionId. +typedef enum SessionType { + kSessionTypeOEMCrypto = 0, + kSessionTypeEntitledKey = 1, +} SessionType; + class CryptoEngine { public: static const uint32_t kApiVersion = 16; - static const uint32_t kMinorApiVersion = 0; + static const uint32_t kMinorApiVersion = 3; static const int64_t kTimeInfoUpdateWindowInSeconds = 300; // This is like a factory method, except we choose which version to use at @@ -83,9 +89,7 @@ class CryptoEngine { size_t DeviceRootTokenLength() { return root_of_trust_.DeviceTokenLength(); } - const uint8_t* DeviceRootToken() { - return root_of_trust_.DeviceToken(); - } + const uint8_t* DeviceRootToken() { return root_of_trust_.DeviceToken(); } virtual void Terminate(); @@ -116,9 +120,7 @@ class CryptoEngine { virtual OEMCrypto_HDCP_Capability config_maximum_hdcp_capability(); // Return true if there might be analog video output enabled. - virtual bool analog_display_active() { - return !config_local_display_only(); - } + virtual bool analog_display_active() { return !config_local_display_only(); } // Return true if there is an analog display, and CGMS A is turned on. virtual bool cgms_a_active() { return false; } @@ -222,6 +224,9 @@ class CryptoEngine { return OEMCrypto_SUCCESS; } + // Get the session type bits from |sid|. + static uint32_t SessionTypeBits(SessionId sid); + protected: // System clock, measuring time in seconds, including anti-rollback offset. int64_t MonotonicTime(); diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp index 28aaa9ac..9c038b6c 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_ref.cpp @@ -39,6 +39,9 @@ namespace { const uint8_t kBakedInCertificateMagicBytes[] = {0xDE, 0xAD, 0xBE, 0xEF}; +// Maximum context key length used for performance reasons, not mandated by +// specification. +const size_t kMaxContextKeyLength = 1024 * 1024; // Return uint32 referenced through a potentially unaligned pointer. // If the pointer is nullptr, return 0. @@ -109,6 +112,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_OpenSession( return OEMCrypto_ERROR_TOO_MANY_SESSIONS; } SessionId sid = crypto_engine->OpenSession(); + if (sid == 0) { + LOGE("OEMCrypto_OpenSession: invalid session id returned."); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } *session = (OEMCrypto_SESSION)sid; return OEMCrypto_SUCCESS; } @@ -141,6 +148,11 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_GenerateDerivedKeys( LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } + if (mac_key_context_length > kMaxContextKeyLength || + enc_key_context_length > kMaxContextKeyLength) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_BUFFER_TOO_LARGE]"); + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } SessionContext* session_ctx = crypto_engine->FindSession(session); if (session_ctx == nullptr || !session_ctx->isValid()) { @@ -723,8 +735,10 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_LoadTestKeybox(const uint8_t* buffer, if (crypto_engine->config_provisioning_method() != OEMCrypto_Keybox) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } - crypto_engine->UseTestKeybox(buffer, length); - return OEMCrypto_SUCCESS; + if (crypto_engine->UseTestKeybox(buffer, length)) { + return OEMCrypto_SUCCESS; + } + return OEMCrypto_ERROR_UNKNOWN_FAILURE; } OEMCRYPTO_API OEMCryptoResult OEMCrypto_IsKeyboxOrOEMCertValid(void) { @@ -887,17 +901,6 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } - // For the reference implementation, the wrapped key and the encrypted - // key are the same size -- just encrypted with different keys. - // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. - // Important: This layout must match OEMCrypto_LoadDRMPrivateKey below. - const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); - - if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) { - *wrapped_rsa_key_length = buffer_size; - return OEMCrypto_ERROR_SHORT_BUFFER; - } - *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. if (!crypto_engine->ValidRootOfTrust()) { LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; @@ -933,13 +936,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( enc_rsa_key_iv, &pkcs8_rsa_key[0])) { return OEMCrypto_ERROR_INVALID_RSA_KEY; } - size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; - if (padding > 16) { - // Do not return an error at this point, to avoid a padding oracle attack. - padding = 0; - } - size_t rsa_key_length = enc_rsa_key_length - padding; - if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) { + if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], enc_rsa_key_length)) { LOGE("[OEMCrypto_RewrapDeviceRSAKey30(): Failed to LoadRSAKey."); return OEMCrypto_ERROR_INVALID_RSA_KEY; } @@ -970,6 +967,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( LOGE("[_RewrapDeviceRSAKey30(): EncrypteRSAKey failed."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } + const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); // The wrapped keybox must be signed with the same key we verify with. I'll // pick the server key, so I don't have to modify LoadRSAKey. unsigned int sig_length = sizeof(wrapped->signature); @@ -1004,17 +1002,6 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( LOGE("[OEMCrypto_RewrapDeviceRSAKey(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return OEMCrypto_ERROR_INVALID_CONTEXT; } - // For the reference implementation, the wrapped key and the encrypted - // key are the same size -- just encrypted with different keys. - // We add 32 bytes for a context, 32 for iv, and 32 bytes for a signature. - // Important: This layout must match OEMCrypto_LoadDRMPrivateKey below. - const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); - - if (wrapped_rsa_key == nullptr || *wrapped_rsa_key_length < buffer_size) { - *wrapped_rsa_key_length = buffer_size; - return OEMCrypto_ERROR_SHORT_BUFFER; - } - *wrapped_rsa_key_length = buffer_size; // Tell caller how much space we used. if (!crypto_engine->ValidRootOfTrust()) { LOGE("[OEMCrypto_RewrapDeviceRSAKey(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; @@ -1051,14 +1038,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( enc_rsa_key_iv, &pkcs8_rsa_key[0])) { return OEMCrypto_ERROR_INVALID_RSA_KEY; } - - size_t padding = pkcs8_rsa_key[enc_rsa_key_length - 1]; - if (padding > 16) { - // Do not return an error at this point, to avoid a padding oracle attack. - padding = 0; - } - size_t rsa_key_length = enc_rsa_key_length - padding; - if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], rsa_key_length)) { + if (!session_ctx->LoadRSAKey(&pkcs8_rsa_key[0], enc_rsa_key_length)) { return OEMCrypto_ERROR_INVALID_RSA_KEY; } @@ -1084,6 +1064,7 @@ static OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey( wrapped->iv, wrapped->enc_rsa_key)) { return OEMCrypto_ERROR_UNKNOWN_FAILURE; } + const size_t buffer_size = enc_rsa_key_length + sizeof(WrappedRSAKey); // The wrapped keybox must be signed with the same key we verify with. I'll // pick the server key, so I don't have to modify LoadRSAKey. unsigned int sig_length = sizeof(wrapped->signature); @@ -1302,6 +1283,12 @@ OEMCRYPTO_API OEMCryptoResult OEMCrypto_DeriveKeysFromSessionKey( LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_KEYBOX_INVALID]"); return OEMCrypto_ERROR_KEYBOX_INVALID; } + if (mac_key_context_length > kMaxContextKeyLength || + enc_key_context_length > kMaxContextKeyLength || + enc_session_key_length > kMaxContextKeyLength) { + LOGE("[OEMCrypto_GenerateDerivedKeys(): ERROR_BUFFER_TOO_LARGE]"); + return OEMCrypto_ERROR_BUFFER_TOO_LARGE; + } SessionContext* session_ctx = crypto_engine->FindSession(session); if (session_ctx == nullptr || !session_ctx->isValid()) { diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.cpp index 4d768ab8..cbbc6fbe 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_session.cpp @@ -86,8 +86,7 @@ class ContentKeysContext : public SessionContextKeys { OEMCrypto_LicenseType type() override { return OEMCrypto_ContentLicense; } - bool SetContentKey(const KeyId& entitlement_id, - const KeyId& content_key_id, + bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key) override; EntitlementKey* GetEntitlementKey(const KeyId& entitlement_id) override; @@ -139,8 +138,7 @@ class EntitlementKeysContext : public SessionContextKeys { Key* FirstKey() override; void Remove(const KeyId& key_id) override; void UpdateDuration(const KeyControlBlock& control) override; - bool SetContentKey(const KeyId& entitlement_id, - const KeyId& content_key_id, + bool SetContentKey(const KeyId& entitlement_id, const KeyId& content_key_id, const std::vector& content_key) override; EntitlementKey* GetEntitlementKey(const KeyId& entitlement_id) override; @@ -210,8 +208,7 @@ SessionContext::SessionContext(CryptoEngine* ce, SessionId sid, CryptoEngine::kApiVersion, sid); } -SessionContext::~SessionContext() { -} +SessionContext::~SessionContext() {} // Internal utility function to derive key using CMAC-128 bool SessionContext::DeriveKey(const std::vector& key, @@ -366,8 +363,10 @@ OEMCryptoResult SessionContext::PrepAndSignLicenseRequest( const size_t message_body_length = message_length - *core_message_length; result = GenerateCertSignature(message_body, message_body_length, signature, signature_length); - if (result == OEMCrypto_SUCCESS) state_request_signed_ = true; - ODK_InitializeClockValues(&clock_values_, ce_->SystemTime()); + if (result == OEMCrypto_SUCCESS) { + state_request_signed_ = true; + result = ODK_InitializeClockValues(&clock_values_, ce_->SystemTime()); + } return result; } @@ -677,11 +676,13 @@ OEMCryptoResult SessionContext::CheckStatusOffline(uint32_t nonce, OEMCryptoResult SessionContext::CheckNonceOrEntry( const KeyControlBlock& key_control_block) { switch (key_control_block.control_bits() & wvoec::kControlReplayMask) { - case wvoec::kControlNonceRequired: // Online license. Nonce always required. + case wvoec::kControlNonceRequired: // Online license. Nonce always + // required. return CheckStatusOnline(key_control_block.nonce(), key_control_block.control_bits()); break; - case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on first use. + case wvoec::kControlNonceOrEntry: // Offline license. Nonce required on + // first use. return CheckStatusOffline(key_control_block.nonce(), key_control_block.control_bits()); break; @@ -844,9 +845,8 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( message + key_array[i].key_control_iv.offset, message + key_array[i].key_control_iv.offset + wvoec::KEY_IV_SIZE); - OEMCryptoResult result = - InstallKey(key_id, enc_key_data, key_data_iv, key_control, - key_control_iv); + OEMCryptoResult result = InstallKey(key_id, enc_key_data, key_data_iv, + key_control, key_control_iv); if (result != OEMCrypto_SUCCESS) { status = result; break; @@ -868,7 +868,12 @@ OEMCryptoResult SessionContext::LoadKeysNoSignature( LOGE("Failed to update mac keys."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } + } else { + // If the mac keys are not updated, we will not need them again. + mac_key_server_.resize(0); + mac_key_client_.resize(0); } + if (usage_entry_) { OEMCryptoResult result = OEMCrypto_SUCCESS; switch (usage_entry_status_) { @@ -1122,6 +1127,10 @@ bool SessionContext::DecryptRSAKey(const uint8_t* enc_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* pkcs8_rsa_key) { + if (enc_rsa_key_length % AES_BLOCK_SIZE != 0) { + LOGE("[DecryptRSAKey(): bad buffer size]"); + return false; + } // Decrypt rsa key with keybox. uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, enc_rsa_key_iv, wvoec::KEY_IV_SIZE); @@ -1136,6 +1145,10 @@ bool SessionContext::EncryptRSAKey(const uint8_t* pkcs8_rsa_key, size_t enc_rsa_key_length, const uint8_t* enc_rsa_key_iv, uint8_t* enc_rsa_key) { + if (enc_rsa_key_length % AES_BLOCK_SIZE != 0) { + LOGE("[EncryptRSAKey(): bad buffer size]"); + return false; + } // Encrypt rsa key with keybox. uint8_t iv_buffer[wvoec::KEY_IV_SIZE]; memcpy(iv_buffer, enc_rsa_key_iv, wvoec::KEY_IV_SIZE); @@ -1227,7 +1240,7 @@ OEMCryptoResult SessionContext::CheckKeyUse(const std::string& log_string, LOGE("[%s(): CGMS required, but buffer is clear", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } - if ( ce_->analog_display_active() && !ce_->cgms_a_active()) { + if (ce_->analog_display_active() && !ce_->cgms_a_active()) { LOGE("[%s(): control bit says CGMS required", log_string.c_str()); return OEMCrypto_ERROR_ANALOG_OUTPUT; } @@ -1252,8 +1265,9 @@ OEMCryptoResult SessionContext::Generic_Encrypt(const uint8_t* in_buffer, LOGE("[Generic_Encrypt(): CONTENT_KEY has wrong size: %zu", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt, - OEMCrypto_BufferType_Clear); + OEMCryptoResult result = + CheckKeyUse("Generic_Encrypt", wvoec::kControlAllowEncrypt, + OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { LOGE("[Generic_Encrypt(): algorithm bad"); @@ -1292,8 +1306,9 @@ OEMCryptoResult SessionContext::Generic_Decrypt(const uint8_t* in_buffer, LOGE("[Generic_Decrypt(): CONTENT_KEY has wrong size"); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt, - OEMCrypto_BufferType_Clear); + OEMCryptoResult result = + CheckKeyUse("Generic_Decrypt", wvoec::kControlAllowDecrypt, + OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_AES_CBC_128_NO_PADDING) { @@ -1373,8 +1388,8 @@ OEMCryptoResult SessionContext::Generic_Verify(const uint8_t* in_buffer, LOGE("[Generic_Verify(): CONTENT_KEY has wrong size: %zu", key.size()); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } - OEMCryptoResult result = CheckKeyUse("Generic_Verify", wvoec::kControlAllowVerify, - OEMCrypto_BufferType_Clear); + OEMCryptoResult result = CheckKeyUse( + "Generic_Verify", wvoec::kControlAllowVerify, OEMCrypto_BufferType_Clear); if (result != OEMCrypto_SUCCESS) return result; if (algorithm != OEMCrypto_HMAC_SHA256) { LOGE("[Generic_Verify(): bad algorithm"); @@ -1523,6 +1538,11 @@ bool SessionContext::DecryptMessage(const std::vector& key, LOGE("[DecryptMessage(): OEMCrypto_ERROR_INVALID_CONTEXT]"); return false; } + if (message.size() % AES_BLOCK_SIZE != 0) { + LOGE("[DecryptMessage(): bad buffer size]"); + return false; + } + decrypted->resize(message.size()); uint8_t iv_buffer[16]; memcpy(iv_buffer, &iv[0], 16); @@ -1714,7 +1734,7 @@ OEMCryptoResult SessionContext::PatternDecryptCBC( const bool skip_block = (pattern_offset >= pattern->encrypt); pattern_offset = (pattern_offset + 1) % pattern_length; if (skip_block || (size < AES_BLOCK_SIZE)) { - // If we are decrypting in-place, then this byte is already correct and + // If we are decrypting in-place, then this block is already correct and // can be skipped. if (clear_data != cipher_data) { memcpy(&clear_data[l], &cipher_data[l], size); diff --git a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp index 08ea1e33..69f2f463 100644 --- a/libwvdrmengine/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp +++ b/libwvdrmengine/oemcrypto/ref/src/oemcrypto_usage_table_ref.cpp @@ -121,7 +121,8 @@ OEMCryptoResult UsageTableEntry::ReportUsage(const std::vector& pst, return OEMCrypto_ERROR_WRONG_PST; } if (CRYPTO_memcmp(&pst[0], data_.pst, data_.pst_length)) { - LOGE("ReportUsage: wrong pst %s, should be %s.", wvcdm::b2a_hex(pst).c_str(), + LOGE("ReportUsage: wrong pst %s, should be %s.", + wvcdm::b2a_hex(pst).c_str(), wvcdm::HexEncode(data_.pst, data_.pst_length).c_str()); return OEMCrypto_ERROR_WRONG_PST; } @@ -283,7 +284,7 @@ OEMCryptoResult UsageTableEntry::LoadData(CryptoEngine* ce, uint32_t index, wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), clear->verification, wvcdm::HexEncode(reinterpret_cast(kEntryVerification), - kMagicLength) + kMagicLength) .c_str(), reinterpret_cast(kEntryVerification)); return OEMCrypto_ERROR_BAD_MAGIC; @@ -378,7 +379,7 @@ OEMCryptoResult UsageTable::LoadUsageEntry( uint32_t index, const std::vector& buffer, ODK_ClockValues* clock_values) { if (!header_loaded_) { - LOGE("CreateNewUsageEntry: Header not loaded."); + LOGE("LoadUsageEntry: Header not loaded."); return OEMCrypto_ERROR_UNKNOWN_FAILURE; } if (!entry) return OEMCrypto_ERROR_UNKNOWN_FAILURE; @@ -557,7 +558,7 @@ OEMCryptoResult UsageTable::LoadUsageTableHeader( wvcdm::HexEncode(clear->verification, kMagicLength).c_str(), clear->verification, wvcdm::HexEncode(reinterpret_cast(kHeaderVerification), - kMagicLength) + kMagicLength) .c_str(), reinterpret_cast(kHeaderVerification)); return OEMCrypto_ERROR_BAD_MAGIC; diff --git a/libwvdrmengine/oemcrypto/test/common.mk b/libwvdrmengine/oemcrypto/test/common.mk index 20015f6e..f531b5c2 100644 --- a/libwvdrmengine/oemcrypto/test/common.mk +++ b/libwvdrmengine/oemcrypto/test/common.mk @@ -13,6 +13,7 @@ LOCAL_SRC_FILES:= \ oec_decrypt_fallback_chain.cpp \ oec_key_deriver.cpp \ oec_session_util.cpp \ + oemcrypto_corpus_generator_helper.cpp \ oemcrypto_session_tests_helper.cpp \ oemcrypto_test.cpp \ oemcrypto_test_android.cpp \ @@ -21,6 +22,7 @@ LOCAL_SRC_FILES:= \ ../../cdm/util/test/test_sleep.cpp \ LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/fuzz_tests \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../odk/include \ $(LOCAL_PATH)/../odk/kdo/include \ diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/README.md b/libwvdrmengine/oemcrypto/test/fuzz_tests/README.md new file mode 100644 index 00000000..98ed2a57 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/README.md @@ -0,0 +1,247 @@ +# OEMCRYPTO Fuzzing + +## Objective + +* Run fuzzing on OEMCrypto public APIs on linux using google supported + clusterfuzz infrastructure to find security vulnerabilities. + + Design Document - https://docs.google.com/document/d/1mdSV2irJZz5Y9uYb5DmSIddBjrAIZU9q8G5Q_BGpA4I/edit?usp=sharing + + Fuzzing at google - + [go/fuzzing](https://g3doc.corp.google.com/security/fuzzing/g3doc/fuzzing_resources.md?cl=head) +## Monitoring +### Cluster fuzz statistics + +* Performance of OEMCrypto fuzz binaries running continuously using cluster + fuzz infrastructure can be monitored + [here](https://clusterfuzz.corp.google.com/fuzzer-stats). + + The options to select are `Job type: libfuzzer_asan_oemcrypto` and `Fuzzer: + fuzzer name you are looking for` + + Example: [load_license_fuzz](https://clusterfuzz.corp.google.com/fuzzer-stats?group_by=by-day&date_start=2020-07-11&date_end=2020-07-17&fuzzer=libFuzzer_oemcrypto_load_license_fuzz&job=libfuzzer_asan_oemcrypto) + +### Issues filed by clusterfuzz - Fixing those issues + +* Any issues found with the fuzz target under test are reported by clusterfuzz + [here](https://b.corp.google.com/hotlists/2442954). + +* The bug will have a link to the test case that generated the bug. Download + the test case and follow the steps from + [testing fuzzer locally](#testing-fuzzer-locally) section to run the fuzzer + locally using the test case that caused the crash. + +* Once the issue is fixed, consider adding the test case that caused the crash + to the seed corpus zip file. Details about seed corpus and their location + are mentioned in + [this section](#build-oemcrypto-unit-tests-to-generate-corpus). + +## Corpus + +* Once the fuzzer scripts are ready and running continuously using clusterfuzz + or android infrastructure, we can measure the efficiency of fuzzers by + looking at code coverage and number of new features that have been + discovered by fuzzer scripts here Fuzz script statistics. + + A fuzzer which tries to start from random inputs and figure out intelligent + inputs to crash the libraries can be time consuming and not effective. A way + to make fuzzers more effective is by providing a set of valid and invalid + inputs of the library so that fuzzer can use those as a starting point. + These sets of valid and invalid inputs are called corpus. + + The idea is to run OEMCrypto unit tests and read required data into binary + corpus files before calling into respective OEMCrypto APIs under test. + Writing corpus data to binary files is controlled by --generate_corpus flag. + +### Build OEMCrypto unit tests to generate corpus + +* Install Pre-requisites + + ```shell + $ sudo apt-get install gyp ninja-build + ``` + +* download cdm source code (including ODK & OEMCrypto unit tests): + + ```shell + $ git clone sso://widevine-internal/cdm + ``` + +* Build OEMCrypto unit tests and run with --generate_corpus flag to generate + corpus files: + + ```shell + $ cd /path/to/cdm/repo + $ export CDM_DIR=/path/to/cdm/repo + $ export PATH_TO_CDM_DIR=.. + $ gyp --format=ninja --depth=$(pwd) oemcrypto/oemcrypto_unittests.gyp + $ ninja -C out/Default/ + $ ./out/Default/oemcrypto_unittests --generate_corpus + ``` + +* To avoid uploading huge binary files to git repository, the corpus files + will be saved in fuzzername_seed_corpus.zip format in blockbuster project's + oemcrypto_fuzzing_corpus GCS bucket using gsutil. If you need permissions + for blockbuster project, contact widevine-engprod@google.com. + + ```shell + $ gsutil cp gs://oemcrypto_fuzzing_corpus/ \ + + ``` + +## Testing fuzzer locally + +* Corpus needed to run fuzz tests locally are available in blockbuster + project's oemcrypto_fuzzing_corpus GCS bucket. If you need permissions for + this project, contact widevine-engprod@google.com. Download corpus. + + ```shell + $ gsutil cp gs://oemcrypto_fuzzing_corpus/ \ + + ``` + +* Add flags to generate additional debugging information. Add '-g3' flag to + oemcrypto_fuzztests.gypi cflags_cc in order to generate additional debug + information locally. + +* Build and test fuzz scripts locally using: + + ```shell + $ export CXX=clang++ + $ export CC=clang + $ export GYP_DEFINES="clang=1" + $ cd /path/to/cdm/repo + $ export PATH_TO_CDM_DIR=. + $ gyp --format=ninja --depth=$(pwd) \ + oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp + $ ninja -C out/Default/ + $ mkdir /tmp/new_interesting_corpus + $ ./out/Default/fuzzer_binary /tmp/new_interesting_corpus \ + /path/to/fuzz/seed/corpus/folder + ``` + +* In order to run fuzz script against a crash input, follow the above steps + and run the fuzz binary against crash input rather than seed corpus. + + ```shell + $ ./out/Default/fuzzer_binary crash_input_file + ``` +## Adding a new OEMCrypto fuzz script +* In order to fuzz a new OEMCrypto API in future, a fuzz script can be added to + oemcrypto/test/fuzz_tests folder which ends with _fuzz.cc. + +* In the program, define the function LLVMFuzzerTestOneInput with the following signature: + ``` + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + + return 0; + } + ``` + *Note*: Make sure LLVMFuzzerTestOneInput calls the function you want to fuzz. + +* Add a new target to oemcrypto_fuzztests.gyp file and follow instructions in + [testing fuzzer locally](#testing-fuzzer-locally) to build and test locally. + +## Building OEMCrypto fuzz scripts and uploading them to Google Cloud Storage: + +* We are using Google Cloud Buid (GCB) in order to setup continuous + integration which uploads OEMCrypto fuzz binaries to Google Cloud Storage. + GCB expects build script in form of a docker image that is uploaded to + Google Container Registry(GCR). + + The cloud build scripts (docker images) for widevine projects are + [here](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker/README.md) + + Refer to README of the project to setup a new docker image and uploading + the image to GCR. + +* Git on borg repository needs to be integrated with GCB and a git trigger + needs to be set up in order to achieve continuous integration. Git trigger + will mention which docker image the GCB needs to use in order to build fuzz + binaries. GCB searches for docker images from GCR. + + Design document lists the steps to create a git trigger. + +### Adding a new fuzz script to the build script: + +* In order to update build script such as adding a new fuzzer to build script, + we need to update the build script in docker image from cloud repository. + [Build script.](https://widevine-internal.googlesource.com/cloud/+/refs/heads/master/docker + /cloud_build/oemcrypto/release/ubuntu/fuzz/build.sh) + + Add the new fuzz script name to fuzzers variable and follow steps in README + to upload new docker image. Make sure you update the tag to be higher than + latest version in GCR. + + Run the following command from your machine to update the docker image tag + in the git trigger. + + ```shell + stubby call --rpc_creds_file=/tmp/mint.txt \ + blade:alphasource-ci-proctor-metadata-service-prod \ + ProctorMetadataService.UpdateTrigger --proto2 <cloud scheduler->oemcrypto_fuzzing_code_coverage_reports and + click on `RUN NOW` button. + +* The above step should invoke a google cloud build. Go to cloud build console + and find latest build job with Trigger Name + `oemcrypto-fuzzing-code-coverage-git-trigger`. + +* Once the build job is successful, latest code coverage reports can be + downloaded from [GCS](https://pantheon.corp.google.com/storage/browser/oemcrypto_fuzzing_code_coverage_reports;tab=objects?forceOnBucketsSortingFiltering=false&project=google.com:blockbuster-1154&prefix=). + The coverage report folder uploaded to GCS is appended with timestamp. diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc new file mode 100644 index 00000000..94d3136d --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_decrypt_cenc_fuzz.cc @@ -0,0 +1,182 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "FuzzedDataProvider.h" +#include "OEMCryptoCENC.h" +#include "log.h" +#include "oemcrypto_fuzz_helper.h" +#include "oemcrypto_fuzz_structs.h" + +namespace wvoec { +const size_t MAX_FUZZ_SAMPLE_SIZE = 5 * MB; +// Free dynamic memory allocated by fuzzer script. +void FreeOutputBuffers(OEMCrypto_SESSION session_id, + OEMCrypto_SampleDescription* sample_description, + size_t sample_index, int* secure_fd_array) { + for (size_t i = 0; i < sample_index; i++) { + OEMCrypto_DestBufferDesc fuzzed_output_descriptor = + sample_description[i].buffers.output_descriptor; + switch (fuzzed_output_descriptor.type) { + case OEMCrypto_BufferType_Clear: { + delete[] fuzzed_output_descriptor.buffer.clear.address; + break; + } + case OEMCrypto_BufferType_Secure: { + OEMCrypto_FreeSecureBuffer(session_id, &fuzzed_output_descriptor, + secure_fd_array[i]); + break; + } + case OEMCrypto_BufferType_Direct: { + break; + } + } + } +} + +// Function to initialize output buffer pointers by allocating memory. +// Limiting output buffer size to 5 MB as 4 MB is maximum size specified +// by resource rating tier documentation. +bool InitializeOutputBuffers(OEMCrypto_SESSION session_id, + OEMCrypto_DestBufferDesc& output_descriptor, + size_t sample_index, + vector& secure_fd_array) { + switch (output_descriptor.type) { + case OEMCrypto_BufferType_Clear: { + output_descriptor.buffer.clear + .address = new OEMCrypto_SharedMemory[std::min( + MAX_FUZZ_SAMPLE_SIZE, output_descriptor.buffer.clear.address_length)]; + return true; + } + case OEMCrypto_BufferType_Secure: { + int* secure_fd; + OEMCryptoResult sts = OEMCrypto_AllocateSecureBuffer( + session_id, + std::min(MAX_FUZZ_SAMPLE_SIZE, + output_descriptor.buffer.secure.handle_length), + &output_descriptor, secure_fd); + if (sts == OEMCrypto_SUCCESS) secure_fd_array[sample_index] = *secure_fd; + return sts == OEMCrypto_SUCCESS; + } + case OEMCrypto_BufferType_Direct: { + return true; + } + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + size_t samples_length; + + // Split data using separator. + auto inputs = SplitInput(data, size); + if (inputs.size() < 2) { + return 0; + } + + OEMCrypto_Decrypt_Cenc_Fuzz fuzzed_structure; + if (inputs[0].size() < sizeof(fuzzed_structure)) { + return 0; + } + // Copy OEMCrypto_Decrypt_Cenc_Fuzz from input data. + memcpy(&fuzzed_structure, data, sizeof(fuzzed_structure)); + ConvertDataToValidEnum(OEMCrypto_CipherMode_MaxValue, + &fuzzed_structure.cipher_mode); + + size_t remaining_size_for_samples = + inputs[0].size() - sizeof(fuzzed_structure); + // Initialize FDP structures to read data using inbuilt functions. + FuzzedDataProvider fuzzed_sample_data(data + sizeof(fuzzed_structure), + remaining_size_for_samples); + FuzzedDataProvider fuzzed_subsample_data(inputs[1].data(), inputs[1].size()); + + // Read subsamples from fuzzed data. + vector subsamples; + while (fuzzed_subsample_data.remaining_bytes() > + sizeof(OEMCrypto_SubSampleDescription)) { + OEMCrypto_SubSampleDescription subsample; + fuzzed_subsample_data.ConsumeData(&subsample, + sizeof(OEMCrypto_SubSampleDescription)); + subsamples.push_back(subsample); + } + if (subsamples.size() == 0) { + return 0; + } + + // Infer samples_length from fuzzed data. + size_t sample_description_size = sizeof(OEMCrypto_SampleDescription); + samples_length = + fuzzed_sample_data.remaining_bytes() / sample_description_size; + if (samples_length == 0) { + return 0; + } + + // Initialize sample_descriptions array. + vector sample_descriptions(samples_length); + // Create array to maintain secure_fd buffer values for secure buffers. + vector secure_fd_array(samples_length); + + OEMCryptoLicenseAPIFuzz license_api_fuzz; + Session* session = license_api_fuzz.session(); + // Copy samples from fuzzed data. + size_t input_subsample_index = 0; + size_t total_input_data_length = 0; + for (size_t i = 0; i < samples_length; i++) { + fuzzed_sample_data.ConsumeData(&sample_descriptions[i], + sample_description_size); + ConvertDataToValidEnum( + OEMCrypto_BufferType_MaxValue, + &sample_descriptions[i].buffers.output_descriptor.type); + + // Copy random data into input sample data. Cap input data length at 5 MB, + // 1 MB higher than that described by resource rating tier. + total_input_data_length += std::min( + MAX_FUZZ_SAMPLE_SIZE, sample_descriptions[i].buffers.input_data_length); + + // Copy sub sample data. + sample_descriptions[i].subsamples = &subsamples[input_subsample_index]; + input_subsample_index += sample_descriptions[i].subsamples_length; + if (input_subsample_index > subsamples.size()) return 0; + } // Sample loop. + + // Allocate input/output buffers for each sample description. + vector input_buffer(total_input_data_length); + RAND_bytes(input_buffer.data(), total_input_data_length); + size_t input_buffer_index = 0; + for (size_t i = 0; i < samples_length; i++) { + sample_descriptions[i].buffers.input_data = + &input_buffer[input_buffer_index]; + input_buffer_index += std::min( + MAX_FUZZ_SAMPLE_SIZE, sample_descriptions[i].buffers.input_data_length); + + // Create output buffer pointers. If secure buffer is not supported, we + // explicitly convert to clear buffer and fuzz. + if (!InitializeOutputBuffers( + session->session_id(), + sample_descriptions[i].buffers.output_descriptor, i, + secure_fd_array)) { + LOGI( + "[OEMCrypto decrypt CENC fuzz] Secure buffers are not supported. Use " + "clear buffer instead."); + sample_descriptions[i].buffers.output_descriptor.type = + OEMCrypto_BufferType_Clear; + InitializeOutputBuffers(session->session_id(), + sample_descriptions[i].buffers.output_descriptor, + i, secure_fd_array); + } + } + + // Load license and call decrypt_cenc API. + license_api_fuzz.LoadLicense(); + OEMCrypto_SelectKey(session->session_id(), session->license().keys[0].key_id, + session->license().keys[0].key_id_length, + fuzzed_structure.cipher_mode); + OEMCrypto_DecryptCENC(session->session_id(), sample_descriptions.data(), + samples_length, &fuzzed_structure.pattern); + FreeOutputBuffers(session->session_id(), sample_descriptions.data(), + samples_length, secure_fd_array.data()); + return 0; +} +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc new file mode 100644 index 00000000..3596d619 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc @@ -0,0 +1,25 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. +#include "oemcrypto_fuzz_helper.h" + +namespace wvoec { +void RedirectStdoutToFile() { freopen("log.txt", "a", stdout); } + +std::vector> SplitInput(const uint8_t* data, size_t size) { + std::vector> result; + auto current_pos = data; + auto end = data + size; + // Using memmem to find separator + while (const uint8_t* pos = reinterpret_cast( + memmem(current_pos, end - current_pos, kFuzzDataSeparator, + sizeof(kFuzzDataSeparator)))) { + result.push_back({current_pos, pos}); + current_pos = pos + sizeof(kFuzzDataSeparator); + } + if (current_pos < end) { + result.push_back({current_pos, end}); + } + return result; +} +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h new file mode 100644 index 00000000..7dc707ee --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.h @@ -0,0 +1,106 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. +#ifndef OEMCRYPTO_FUZZ_HELPER_H_ +#define OEMCRYPTO_FUZZ_HELPER_H_ + +#include + +#include "FuzzedDataProvider.h" +#include "OEMCryptoCENC.h" +#include "oec_device_features.h" +#include "oemcrypto_corpus_generator_helper.h" +#include "oemcrypto_session_tests_helper.h" + +namespace wvoec { +// Initial setup to create a valid OEMCrypto state such as initializing crypto +// firmware/hardware, installing golden key box etc. in order to fuzz +// OEMCrypto APIs. +class InitializeFuzz : public SessionUtil { + public: + InitializeFuzz() { + wvoec::global_features.Initialize(); + OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); + OEMCrypto_Initialize(); + EnsureTestKeys(); + } + + ~InitializeFuzz() { OEMCrypto_Terminate(); } +}; + +class OEMCryptoLicenseAPIFuzz : public InitializeFuzz { + public: + OEMCryptoLicenseAPIFuzz() : license_messages_(&session_) { + session_.open(); + InstallTestRSAKey(&session_); + session_.GenerateNonce(); + } + + ~OEMCryptoLicenseAPIFuzz() { + session_.close(); + } + + LicenseRoundTrip& license_messages() { return license_messages_; } + + Session* session() { return &session_; } + + void LoadLicense() { + license_messages_.SignAndVerifyRequest(); + license_messages_.CreateDefaultResponse(); + license_messages_.EncryptAndSignResponse(); + license_messages_.LoadResponse(); + } + + private: + Session session_; + LicenseRoundTrip license_messages_; +}; + +class OEMCryptoProvisioningAPIFuzz : public InitializeFuzz { + public: + OEMCryptoProvisioningAPIFuzz() + : provisioning_messages_(&session_, encoded_rsa_key_) { + // Opens a session and Generates Nonce. + provisioning_messages_.PrepareSession(keybox_); + } + + ~OEMCryptoProvisioningAPIFuzz() { session_.close(); } + + ProvisioningRoundTrip& provisioning_messages() { + return provisioning_messages_; + } + + private: + Session session_; + ProvisioningRoundTrip provisioning_messages_; +}; + +// Initial setup to create a valid state such as creating session, installing +// golden key box etc. in order to fuzz Load Renewal API. +class OEMCryptoRenewalAPIFuzz : public OEMCryptoLicenseAPIFuzz { + public: + OEMCryptoRenewalAPIFuzz() : renewal_messages_(&license_messages()) {} + + RenewalRoundTrip& renewal_messages() { return renewal_messages_; } + + private: + RenewalRoundTrip renewal_messages_; +}; + +// Convert data to valid enum value. +template +void ConvertDataToValidEnum(T max_enum_value, T* t) { + FuzzedDataProvider fuzzed_enum_data(reinterpret_cast(t), sizeof(T)); + *t = static_cast(fuzzed_enum_data.ConsumeIntegralInRange( + 0, static_cast(max_enum_value))); +} + +// Redirect printf and log statements from oemcrypto functions to a file to +// reduce noise +void RedirectStdoutToFile(); + +// Function to split fuzzer input using delimiter "-_^_". +std::vector> SplitInput(const uint8_t* data, size_t size); +} // namespace wvoec + +#endif // OEMCRYPTO_FUZZ_HELPER_H_ diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h new file mode 100644 index 00000000..52fb400d --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_structs.h @@ -0,0 +1,42 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. +#ifndef OEMCRYPTO_FUZZ_STRUCTS_H_ +#define OEMCRYPTO_FUZZ_STRUCTS_H_ + +namespace wvoec { +struct OEMCrypto_Renewal_Response_Fuzz { + // Timer limits in core license response needs to be fuzzed as load renewal + // depends on timer limits loaded from license response. + ODK_TimerLimits timer_limits; + // message(core_response + license_renewal_response) which mimics + // response from license renewal server needs to be fuzzed. core_request + // will be used to generate serialized core response. + oemcrypto_core_message::ODK_RenewalRequest core_request; + // Renewal duration seconds needs to be fuzzed which is part of serialized + // core message from license renewal server. + uint64_t renewal_duration_seconds; + // license_renewal_response is of variable length and not included in this + // structure. +}; + +struct OEMCrypto_Request_Fuzz { + // We would like to fuzz computed signature_length, input core_message_length + // that ODK parses and actual message buffer to the request APIs. + size_t signature_length; + size_t core_message_length; + // Request message is of variable length and not included in this structure. +}; + +struct OEMCrypto_Decrypt_Cenc_Fuzz { + // Corpus format is as below, let | be separator. + // cipher_mode + pattern + sample_data for all samples | + // subsample_data for all samples + OEMCryptoCipherMode cipher_mode; + OEMCrypto_CENCEncryptPatternDesc pattern; + // Sample data and subsample data are of variable length and not included in + // this structure. +}; +} // namespace wvoec + +#endif // OEMCRYPTO_FUZZ_STRUCTS_H_ \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc new file mode 100644 index 00000000..202351eb --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc @@ -0,0 +1,36 @@ +#include "properties.h" +#include "oemcrypto_session_tests_helper.h" + +using namespace wvoec; + +static bool is_init = false; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SessionUtil session_helper; + if (!is_init) { + wvoec::global_features.Initialize(); + wvoec::global_features.RestrictFilter("*"); + wvcdm::Properties::Init(); + is_init = true; + } + + OEMCrypto_Initialize(); + session_helper.EnsureTestKeys(); + + Session s; + s.open(); + s.GenerateDerivedKeysFromKeybox(session_helper.keybox_); + + static const uint32_t SignatureBufferMaxLength = size; + vector signature(SignatureBufferMaxLength); + size_t signature_length = signature.size(); + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature(s.session_id(), data, size, + &signature[0], &signature_length); + + s.close(); + OEMCrypto_Terminate(); + + return 0; +} diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc new file mode 100644 index 00000000..d100ddea --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_license_request_fuzz.cc @@ -0,0 +1,28 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "oemcrypto_fuzz_helper.h" +#include "oemcrypto_fuzz_structs.h" + +namespace wvoec { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + // Reject the input if it is less than fuzz data structure size. + if (size < sizeof(OEMCrypto_Request_Fuzz)) { + return 0; + } + // Input for license request API will be modified by OEMCrypto, hence it + // cannot be a const. Fuzzer complains if const identifier is removed of data, + // hence copying data into a non const pointer. + uint8_t* input = new uint8_t[size]; + memcpy(input, data, size); + OEMCryptoLicenseAPIFuzz license_api_fuzz; + license_api_fuzz.license_messages().InjectFuzzedRequestData(input, size); + delete[] input; + return 0; +} +} // namespace wvoec \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc new file mode 100644 index 00000000..892d6e93 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_entitled_content_keys_fuzz.cc @@ -0,0 +1,66 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "FuzzedDataProvider.h" +#include "oemcrypto_fuzz_helper.h" +#include "oemcrypto_fuzz_structs.h" + +namespace wvoec { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + + // Corpus format is as below, let | be separator. + // message buffer with key data | entitled content key object array with + // offsets and lengths to read key data from message buffer. + // Split data using separator. + auto inputs = SplitInput(data, size); + if (inputs.size() < 2) { + return 0; + } + + FuzzedDataProvider fuzzed_entitled_content_key_array(inputs[1].data(), + inputs[1].size()); + + // Message to be verified. Return 0 if key data buffer is empty. + if (inputs[0].size() == 0) { + return 0; + } + + // Copy data to OEMCrypto_EntitledContentKeyObject array. + size_t entitled_content_key_object_size = + sizeof(OEMCrypto_EntitledContentKeyObject); + size_t entitled_content_key_array_length = + fuzzed_entitled_content_key_array.remaining_bytes() / + entitled_content_key_object_size; + if (entitled_content_key_array_length == 0) { + return 0; + } + OEMCrypto_EntitledContentKeyObject* entitled_content_key_array = + new OEMCrypto_EntitledContentKeyObject[entitled_content_key_array_length]; + + for (size_t i = 0; i < entitled_content_key_array_length; i++) { + fuzzed_entitled_content_key_array.ConsumeData( + &entitled_content_key_array[i], entitled_content_key_object_size); + } + + OEMCryptoLicenseAPIFuzz license_api_fuzz; + // Setting up state. Load default entitlement license to load entitlement + // keys into sessions key table. + license_api_fuzz.license_messages().set_license_type( + OEMCrypto_EntitlementLicense); + license_api_fuzz.LoadLicense(); + // Call OEMCrypto_LoadEntitledContentKeys with fuzzed buffers. + Session* session = license_api_fuzz.session(); + uint8_t* fuzzed_key_data = inputs[0].data(); + size_t fuzzed_key_data_size = inputs[0].size(); + OEMCrypto_LoadEntitledContentKeys( + session->session_id(), fuzzed_key_data, fuzzed_key_data_size, + entitled_content_key_array_length, entitled_content_key_array); + delete[] entitled_content_key_array; + return 0; +} +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc new file mode 100644 index 00000000..e125ae04 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_license_fuzz.cc @@ -0,0 +1,30 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "oemcrypto_fuzz_helper.h" + +namespace wvoec { +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + if (size < sizeof(ODK_ParsedLicense) + sizeof(MessageData)) { + return 0; + } + OEMCryptoLicenseAPIFuzz license_api_fuzz; + license_api_fuzz.license_messages().SignAndVerifyRequest(); + // Interpreting input fuzz data as unencrypted (core_response + license + // message data) from license server. + license_api_fuzz.license_messages().InjectFuzzedResponseData(data, size); + + // Convert OEMCrypto_LicenseType in core_response to a valid enum value. + ConvertDataToValidEnum( + OEMCrypto_LicenstType_MaxValue, + &license_api_fuzz.license_messages().core_response().license_type); + + license_api_fuzz.license_messages().EncryptAndSignResponse(); + license_api_fuzz.license_messages().LoadResponse(); + return 0; +} +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc new file mode 100644 index 00000000..739f79f4 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_provisioning_fuzz.cc @@ -0,0 +1,27 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "oemcrypto_fuzz_helper.h" + +namespace wvoec { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + if (size < sizeof(ODK_ParsedProvisioning) + sizeof(RSAPrivateKeyMessage)) { + return 0; + } + + OEMCryptoProvisioningAPIFuzz provisioning_api_fuzz; + provisioning_api_fuzz.provisioning_messages().SignAndVerifyRequest(); + // Interpreting input fuzz data as unencrypted(core_response + provisioning + // message data) from provisioning server. + provisioning_api_fuzz.provisioning_messages().InjectFuzzedResponseData(data, + size); + provisioning_api_fuzz.provisioning_messages().EncryptAndSignResponse(); + provisioning_api_fuzz.provisioning_messages().LoadResponse(); + return 0; +} +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc new file mode 100644 index 00000000..f521b477 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_load_renewal_fuzz.cc @@ -0,0 +1,42 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "oemcrypto_fuzz_helper.h" +#include "oemcrypto_fuzz_structs.h" + +namespace wvoec { + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + if (size < sizeof(OEMCrypto_Renewal_Response_Fuzz)) { + return 0; + } + // Copy input data to OEMCrypto_Renewal_Response_Fuzz and rest of message + // into encrypted license_renewal_response. + OEMCrypto_Renewal_Response_Fuzz fuzzed_data; + memcpy(&fuzzed_data, data, sizeof(fuzzed_data)); + const uint8_t* renewal_response = + data + sizeof(OEMCrypto_Renewal_Response_Fuzz); + const size_t renewal_response_size = + size - sizeof(OEMCrypto_Renewal_Response_Fuzz); + + OEMCryptoRenewalAPIFuzz renewal_response_fuzz; + renewal_response_fuzz.license_messages().SignAndVerifyRequest(); + renewal_response_fuzz.license_messages().CreateDefaultResponse(); + // Inject timer limits from fuzzed input to timer_limits field from + // core license response. + renewal_response_fuzz.license_messages().InjectFuzzedTimerLimits(fuzzed_data); + renewal_response_fuzz.license_messages().EncryptAndSignResponse(); + renewal_response_fuzz.license_messages().LoadResponse(); + + // Call renewal response API using fuzzed data. + renewal_response_fuzz.renewal_messages().SignAndVerifyRequest(); + renewal_response_fuzz.renewal_messages().InjectFuzzedResponseData( + fuzzed_data, renewal_response, renewal_response_size); + renewal_response_fuzz.renewal_messages().LoadResponse(); + return 0; +} +} // namespace wvoec \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc new file mode 100644 index 00000000..1cda2abd --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_provisioning_request_fuzz.cc @@ -0,0 +1,28 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "oemcrypto_fuzz_helper.h" +#include "oemcrypto_fuzz_structs.h" + +namespace wvoec { +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + // If input size is less than fuzz data structure size, reject the input. + if (size < sizeof(OEMCrypto_Request_Fuzz)) { + return 0; + } + // Input for provisioning request API will be modified by OEMCrypto, hence it + // cannot be a const. Fuzzer complains if const identifier is removed of data, + // hence copying data into a non const pointer. + uint8_t* input = new uint8_t[size]; + memcpy(input, data, size); + OEMCryptoProvisioningAPIFuzz provisioning_api_fuzz; + provisioning_api_fuzz.provisioning_messages().InjectFuzzedRequestData(input, + size); + delete[] input; + return 0; +} +} // namespace wvoec \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc new file mode 100644 index 00000000..67ecb0a9 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/oemcrypto_renewal_request_fuzz.cc @@ -0,0 +1,27 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "oemcrypto_fuzz_helper.h" +#include "oemcrypto_fuzz_structs.h" + +namespace wvoec { +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + // Redirect printf and log statements from oemcrypto functions to a file to + // reduce noise + RedirectStdoutToFile(); + // If input size is less than fuzz data structure, reject the input. + if (size < sizeof(OEMCrypto_Request_Fuzz)) { + return 0; + } + // Input for renewal request API will be modified by OEMCrypto, hence it + // cannot be a const. Fuzzer complains if const identifier is removed of data, + // hence copying data into a non const pointer. + uint8_t* input = new uint8_t[size]; + memcpy(input, data, size); + OEMCryptoRenewalAPIFuzz renewal_api_fuzz; + renewal_api_fuzz.renewal_messages().InjectFuzzedRequestData(input, size); + delete[] input; + return 0; +} +} // namespace wvoec \ No newline at end of file diff --git a/libwvdrmengine/oemcrypto/test/fuzz_tests/sample_test.cc b/libwvdrmengine/oemcrypto/test/fuzz_tests/sample_test.cc new file mode 100644 index 00000000..68903939 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/fuzz_tests/sample_test.cc @@ -0,0 +1,10 @@ +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + __builtin_trap(); + return 0; +} diff --git a/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.cpp b/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.cpp index e8d82477..7c589c51 100644 --- a/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.cpp @@ -7,6 +7,7 @@ #include #include +#include "oemcrypto_corpus_generator_helper.h" #include "oemcrypto_types.h" #include "string_conversions.h" @@ -55,7 +56,12 @@ OEMCryptoResult DecryptFallbackChain::Decrypt( OEMCrypto_DecryptCENC(session_id, samples, samples_length, pattern); // No need for a fallback. Abort early. - if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts; + if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) { + if (ShouldGenerateCorpus()) { + WriteDecryptCencCorpus(cipher_mode, samples, pattern, samples_length); + } + return sts; + } // Fall back to decrypting individual samples. for (size_t i = 0; i < samples_length; ++i) { @@ -75,7 +81,12 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample( OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern); // No need for a fallback. Abort early. - if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts; + if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) { + if (ShouldGenerateCorpus()) { + WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1); + } + return sts; + } // Fall back to decrypting individual subsamples. OEMCrypto_SampleDescription fake_sample = sample; @@ -88,7 +99,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample( fake_sample.subsamples = &subsample; fake_sample.subsamples_length = 1; - sts = DecryptSubsample(session_id, fake_sample, pattern); + sts = DecryptSubsample(session_id, fake_sample, pattern, cipher_mode); if (sts != OEMCrypto_SUCCESS) return sts; fake_sample.buffers.input_data += length; @@ -106,11 +117,17 @@ OEMCryptoResult DecryptFallbackChain::DecryptSample( // OEMCrypto implementation does not accept full subsamples. OEMCryptoResult DecryptFallbackChain::DecryptSubsample( OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample, - const OEMCrypto_CENCEncryptPatternDesc* pattern) { + const OEMCrypto_CENCEncryptPatternDesc* pattern, + OEMCryptoCipherMode cipher_mode) { OEMCryptoResult sts = OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern); // No need for a fallback. Abort early. - if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) return sts; + if (sts != OEMCrypto_ERROR_BUFFER_TOO_LARGE) { + if (ShouldGenerateCorpus()) { + WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1); + } + return sts; + } // Fall back to decrypting individual subsample halves. const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0]; @@ -132,7 +149,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample( subsample.num_bytes_encrypted == 0) fake_subsample.subsample_flags |= OEMCrypto_LastSubsample; - sts = DecryptSubsampleHalf(session_id, fake_sample, pattern); + sts = DecryptSubsampleHalf(session_id, fake_sample, pattern, cipher_mode); if (sts != OEMCrypto_SUCCESS) return sts; // Advance the buffers for the other half, in case they're needed. @@ -154,7 +171,7 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample( if (subsample.subsample_flags & OEMCrypto_LastSubsample) fake_subsample.subsample_flags |= OEMCrypto_LastSubsample; - sts = DecryptSubsampleHalf(session_id, fake_sample, pattern); + sts = DecryptSubsampleHalf(session_id, fake_sample, pattern, cipher_mode); if (sts != OEMCrypto_SUCCESS) return sts; } @@ -166,7 +183,11 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsample( // caller. OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf( OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample, - const OEMCrypto_CENCEncryptPatternDesc* pattern) { + const OEMCrypto_CENCEncryptPatternDesc* pattern, + OEMCryptoCipherMode cipher_mode) { + if (ShouldGenerateCorpus()) { + WriteDecryptCencCorpus(cipher_mode, &sample, pattern, 1); + } return OEMCrypto_DecryptCENC(session_id, &sample, 1, pattern); // In a real CDM, you would want some fallback here to handle the case where // the buffer is too big for the OEMCrypto implementation. But in the case of @@ -175,4 +196,40 @@ OEMCryptoResult DecryptFallbackChain::DecryptSubsampleHalf( // here. } +// Used for OEMCrypto Fuzzing: Corpus format is as below, let | be separator. +// cipher_mode + pattern + sample_data for all samples | +// subsample_data for all samples +void WriteDecryptCencCorpus( + OEMCryptoCipherMode cipher_mode, + const OEMCrypto_SampleDescription* samples_description, + const OEMCrypto_CENCEncryptPatternDesc* pattern, size_t samples_length) { + const std::string file_name = + GetFileName("oemcrypto_decrypt_cenc_fuzz_seed_corpus"); + // Cipher mode. + AppendToFile(file_name, reinterpret_cast(&cipher_mode), + sizeof(OEMCryptoCipherMode)); + + // Pattern. + AppendToFile(file_name, reinterpret_cast(pattern), + sizeof(OEMCrypto_CENCEncryptPatternDesc)); + + // Sample data for all samples. + for (size_t i = 0; i < samples_length; i++) { + AppendToFile(file_name, + reinterpret_cast(&samples_description[i]), + sizeof(OEMCrypto_SampleDescription)); + } + AppendSeparator(file_name); + + // Subsample data for all samples. + for (size_t i = 0; i < samples_length; i++) { + for (size_t j = 0; j < samples_description[i].subsamples_length; j++) { + AppendToFile( + file_name, + reinterpret_cast(&samples_description[i].subsamples[j]), + sizeof(OEMCrypto_SubSampleDescription)); + } + } +} + } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.h b/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.h index 0e1512b1..2379dafc 100644 --- a/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.h +++ b/libwvdrmengine/oemcrypto/test/oec_decrypt_fallback_chain.h @@ -43,17 +43,24 @@ class DecryptFallbackChain { static OEMCryptoResult DecryptSubsample( OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample, - const OEMCrypto_CENCEncryptPatternDesc* pattern); + const OEMCrypto_CENCEncryptPatternDesc* pattern, + OEMCryptoCipherMode cipher_mode); static OEMCryptoResult DecryptSubsampleHalf( OEMCrypto_SESSION session_id, const OEMCrypto_SampleDescription& sample, - const OEMCrypto_CENCEncryptPatternDesc* pattern); + const OEMCrypto_CENCEncryptPatternDesc* pattern, + OEMCryptoCipherMode cipher_mode); // There is no reason to have an instance of this class. DecryptFallbackChain() = delete; CORE_DISALLOW_COPY_AND_ASSIGN(DecryptFallbackChain); }; +void WriteDecryptCencCorpus(OEMCryptoCipherMode cipher_mode, + const OEMCrypto_SampleDescription* samples, + const OEMCrypto_CENCEncryptPatternDesc* pattern, + size_t samples_length); + } // namespace wvoec #endif // CDM_OEC_DECRYPT_FALLBACK_CHAIN_H_ diff --git a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp index de1fcf43..b4988edd 100644 --- a/libwvdrmengine/oemcrypto/test/oec_device_features.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_device_features.cpp @@ -32,6 +32,7 @@ void DeviceFeatures::Initialize() { return; } uint8_t buffer[1]; + uint8_t iv[16] = {}; size_t size = 0; provisioning_method = OEMCrypto_GetProvisioningMethod(); printf("provisioning_method = %s\n", @@ -58,12 +59,12 @@ void DeviceFeatures::Initialize() { printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false"); generic_crypto = (OEMCrypto_ERROR_NOT_IMPLEMENTED != - OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer, + OEMCrypto_Generic_Encrypt(session, buffer, 0, iv, OEMCrypto_AES_CBC_128_NO_PADDING, buffer)); printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false"); OEMCrypto_CloseSession(session); api_version = OEMCrypto_APIVersion(); - printf("api_version = %d.\n", api_version); + printf("api_version = %u.\n", api_version); // These unit tests only work with new usage tables. We do not test v12 // usage tables. if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable(); @@ -80,7 +81,7 @@ void DeviceFeatures::Initialize() { } printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false"); resource_rating = OEMCrypto_ResourceRatingTier(); - printf("resource_rating = %d, security level %s.\n", resource_rating, + printf("resource_rating = %u, security level %s.\n", resource_rating, OEMCrypto_SecurityLevel()); uint32_t decrypt_hash_type = OEMCrypto_SupportsDecryptHash(); supports_crc = (decrypt_hash_type == OEMCrypto_CRC_Clear_Buffer); @@ -114,8 +115,7 @@ void DeviceFeatures::Initialize() { std::string security_level = OEMCrypto_SecurityLevel(); supports_level_1 = (security_level == "L1"); printf("SecurityLevel is %s (%s)\n", - supports_level_1 ? "Level 1" : "Not Level 1", - security_level.c_str()); + supports_level_1 ? "Level 1" : "Not Level 1", security_level.c_str()); CheckSecureBuffers(); OEMCrypto_Terminate(); initialized_ = true; diff --git a/libwvdrmengine/oemcrypto/test/oec_key_deriver.cpp b/libwvdrmengine/oemcrypto/test/oec_key_deriver.cpp index 34fb0fe5..d2bff254 100644 --- a/libwvdrmengine/oemcrypto/test/oec_key_deriver.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_key_deriver.cpp @@ -61,6 +61,12 @@ void Encryptor::PadAndEncryptProvisioningMessage( EXPECT_EQ(1, GetRandBytes(data->rsa_key_iv, KEY_IV_SIZE)); ASSERT_EQ(enc_key_.size(), KEY_SIZE); *encrypted = *data; + if (data->rsa_key_length > sizeof(data->rsa_key)) { + // OEMCrypto Fuzzing: fuzzed |rsa_key_length| overflows the allocated + // buffer. Skip encryption in that case. + return; + } + size_t padding = AES_BLOCK_SIZE - (data->rsa_key_length % AES_BLOCK_SIZE); memset(data->rsa_key + data->rsa_key_length, static_cast(padding), padding); diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 6c18aea9..5dd0299b 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -31,8 +31,11 @@ #include "core_message_serialize.h" #include "disallow_copy_and_assign.h" #include "log.h" +#include "odk_attributes.h" +#include "odk_structs.h" #include "oec_device_features.h" #include "oec_test_data.h" +#include "oemcrypto_corpus_generator_helper.h" #include "oemcrypto_types.h" #include "platform.h" #include "string_conversions.h" @@ -162,6 +165,10 @@ void RoundTrip(gen_signature_length, + core_message_length, data); + } vector gen_signature(gen_signature_length); ASSERT_EQ(PrepAndSignRequest(session()->session_id(), data.data(), @@ -177,6 +184,27 @@ void RoundTrip +void RoundTrip::InjectFuzzedRequestData(uint8_t* data, + size_t size) { + OEMCrypto_Request_Fuzz fuzz_structure; + // Copy data into fuzz structure, cap signature length at 1mb as it will be + // used to initialize signature vector. + memcpy(&fuzz_structure, data, sizeof(fuzz_structure)); + fuzz_structure.signature_length = + std::min(fuzz_structure.signature_length, MB); + vector signature(fuzz_structure.signature_length); + + // Interpret rest of data as actual message buffer to request APIs. + uint8_t* message_ptr = data + sizeof(fuzz_structure); + size_t message_size = size - sizeof(fuzz_structure); + PrepAndSignRequest(session()->session_id(), message_ptr, message_size, + &fuzz_structure.core_message_length, signature.data(), + &fuzz_structure.signature_length); +} + template OEMCrypto_Substring RoundTripnonce(); +} + OEMCryptoResult ProvisioningRoundTrip::LoadResponse(Session* session) { EXPECT_NE(session, nullptr); + // Write corpus for oemcrypto_load_provisioning_fuzz. Fuzz script expects + // unencrypted response from provisioning server as input corpus data. + // Data will be encrypted and signed again explicitly by fuzzer script after + // mutations. + if (ShouldGenerateCorpus()) { + const std::string file_name = + GetFileName("oemcrypto_load_provisioning_fuzz_seed_corpus"); + // Corpus for license response fuzzer should be in the format: + // unencrypted (core_response + response_data). + AppendToFile(file_name, reinterpret_cast(&core_response_), + sizeof(ODK_ParsedProvisioning)); + AppendToFile(file_name, reinterpret_cast(&response_data_), + sizeof(response_data_)); + } size_t wrapped_key_length = 0; const OEMCryptoResult sts = LoadResponseNoRetry(session, &wrapped_key_length); if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return sts; @@ -472,6 +527,69 @@ void LicenseRoundTrip::CreateDefaultResponse() { FillCoreResponseSubstrings(); } +void LicenseRoundTrip::ConvertDataToValidBools(ODK_ParsedLicense* t) { + t->nonce_required = ConvertByteToValidBoolean(&t->nonce_required); + t->timer_limits.soft_enforce_playback_duration = ConvertByteToValidBoolean( + &t->timer_limits.soft_enforce_playback_duration); + t->timer_limits.soft_enforce_rental_duration = + ConvertByteToValidBoolean(&t->timer_limits.soft_enforce_rental_duration); +} + +void LicenseRoundTrip::InjectFuzzedTimerLimits( + OEMCrypto_Renewal_Response_Fuzz& fuzzed_data) { + // Interpreting fuzz data as timer limits. + // Copy timer limits from data. + memcpy(&core_response_.timer_limits, &fuzzed_data.timer_limits, + sizeof(fuzzed_data.timer_limits)); + ConvertDataToValidBools(&core_response_); +} + +void LicenseRoundTrip::InjectFuzzedResponseData(const uint8_t* data, + size_t size UNUSED) { + // Interpreting fuzz data as unencrypted core_response + message_data + const size_t core_response_size = sizeof(ODK_ParsedLicense); + // Copy core_response from data. + memcpy(&core_response_, data, core_response_size); + // Maximum number of keys could be kMaxNumKeys(30). key_array_length can be + // any random value as it is read from fuzz data. + // Key data array(MessageKeyData keys[kMaxNumKeys]) will be looped over + // key_array_length number of times during LoadLicense. If key_array_length is + // more than kMaxNumKeys, setting it to max value of kMaxNumKeys as we should + // not go out of bounds of this array length. For corpus, this value is + // already hard coded to 4. + if (core_response_.key_array_length > kMaxNumKeys) { + core_response_.key_array_length = kMaxNumKeys; + } + // For corpus data, this value gets set to 4, but we need to test other + // scenarios too, hence reading key_array_length value. + set_num_keys(core_response_.key_array_length); + ConvertDataToValidBools(&core_response_); + // TODO(b/157520981): Once assertion bug is fixed, for loop can be removed. + // Workaround for the above bug: key_data.length and key_id.length are being + // used in AES decryption process and are expected to be a multiple of 16. An + // assertion in AES decryption fails if this condition is not met which will + // crash fuzzer. + for (uint32_t i = 0; i < num_keys_; ++i) { + size_t key_data_length = core_response_.key_array[i].key_data.length; + size_t key_id_length = core_response_.key_array[i].key_id.length; + if (key_data_length % 16 != 0) { + core_response_.key_array[i].key_data.length = + key_data_length - (key_data_length % 16); + } + if (key_id_length % 16 != 0) { + core_response_.key_array[i].key_id.length = + key_id_length - (key_id_length % 16); + } + } + + // Copy response_data from data and set nonce to match one in request to pass + // nonce validations. + memcpy(&response_data_, data + core_response_size, sizeof(response_data_)); + for (uint32_t i = 0; i < num_keys_; ++i) { + response_data_.keys[i].control.nonce = session()->nonce(); + } +} + void LicenseRoundTrip::CreateResponseWithGenericCryptoKeys() { CreateDefaultResponse(); response_data_.keys[0].control.control_bits |= @@ -540,17 +658,28 @@ void LicenseRoundTrip::EncryptAndSignResponse() { 2 * MAC_KEY_SIZE, response_data_.mac_key_iv); for (unsigned int i = 0; i < num_keys_; i++) { - memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE); - AES_KEY aes_key; - AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key); - AES_cbc_encrypt( - reinterpret_cast(&response_data_.keys[i].control), - reinterpret_cast(&encrypted_response_data_.keys[i].control), - KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); - session_->key_deriver().CBCEncrypt( - &response_data_.keys[i].key_data[0], - &encrypted_response_data_.keys[i].key_data[0], - response_data_.keys[i].key_data_length, response_data_.keys[i].key_iv); + // OEMCrypto Fuzzing skip encryption: key_data_length can be any number when + // called from fuzzer. We want to skip encryption if that happens and let + // LoadLicense be called with unencrypted data for that key. OEMCrypto + // Fuzzing skip encryption: key_data_length being a random value will + // encrypt data which is not expected to, there by leading to inefficient + // fuzzing. + if (response_data_.keys[i].key_data_length <= + sizeof(response_data_.keys[i].key_data) && + response_data_.keys[i].key_data_length % 16 == 0) { + memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE); + AES_KEY aes_key; + AES_set_encrypt_key(&response_data_.keys[i].key_data[0], 128, &aes_key); + AES_cbc_encrypt( + reinterpret_cast(&response_data_.keys[i].control), + reinterpret_cast(&encrypted_response_data_.keys[i].control), + KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); + session_->key_deriver().CBCEncrypt( + &response_data_.keys[i].key_data[0], + &encrypted_response_data_.keys[i].key_data[0], + response_data_.keys[i].key_data_length, + response_data_.keys[i].key_iv); + } } if (api_version_ < kCoreMessagesAPI) { serialized_core_message_.resize(0); @@ -588,6 +717,21 @@ void LicenseRoundTrip::EncryptAndSignResponse() { OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) { EXPECT_NE(session, nullptr); + // Write corpus for oemcrypto_load_license_fuzz. Fuzz script expects + // unecnrypted response from license server as input corpus data. + // Data will be encrypted and signed again explicitly by fuzzer script + // after mutations. + if (ShouldGenerateCorpus()) { + const std::string file_name = + GetFileName("oemcrypto_load_license_fuzz_seed_corpus"); + // Corpus for license response fuzzer should be in the format: + // core_response + response_data. + AppendToFile(file_name, reinterpret_cast(&core_response_), + sizeof(ODK_ParsedLicense)); + AppendToFile(file_name, reinterpret_cast(&response_data_), + sizeof(response_data_)); + } + // Some tests adjust the offset to be beyond the length of the message. Here, // we create a duplicate of the main message buffer so that these offsets do // not point to garbage data. The goal is to make sure OEMCrypto is verifying @@ -757,6 +901,17 @@ void EntitledMessage::LoadKeys(OEMCryptoResult expected_sts) { key_data->encrypted_content_key_data, KEY_SIZE, &aes_key, iv, AES_ENCRYPT); } + if (ShouldGenerateCorpus()) { + const std::string file_name = + GetFileName("oemcrypto_load_entitled_content_keys_fuzz_seed_corpus"); + // Corpus for load entitled keys fuzzer should be in the format: + // message buffer to be verified | entitled content key object array. + AppendToFile(file_name, reinterpret_cast(entitled_key_data_), + sizeof(entitled_key_data_)); + AppendSeparator(file_name); + AppendToFile(file_name, reinterpret_cast(entitled_key_array_), + num_keys_); + } ASSERT_EQ(expected_sts, OEMCrypto_LoadEntitledContentKeys( license_messages_->session()->session_id(), @@ -906,7 +1061,46 @@ void RenewalRoundTrip::EncryptAndSignResponse() { &response_signature_); } +void RenewalRoundTrip::InjectFuzzedResponseData( + OEMCrypto_Renewal_Response_Fuzz& fuzzed_data, + const uint8_t* renewal_response, const size_t renewal_response_size) { + // Serializing core message. + // This call also sets nonce in core response to match with session nonce. + oemcrypto_core_message::serialize::CreateCoreRenewalResponse( + fuzzed_data.core_request, fuzzed_data.renewal_duration_seconds, + &serialized_core_message_); + + // Copy serialized core message and encrypted response from data and + // calculate signature. Now we will have a valid signature for data generated + // by fuzzer. + encrypted_response_.assign(serialized_core_message_.begin(), + serialized_core_message_.end()); + encrypted_response_.insert(encrypted_response_.end(), renewal_response, + renewal_response + renewal_response_size); + session()->key_deriver().ServerSignBuffer(encrypted_response_.data(), + encrypted_response_.size(), + &response_signature_); +} + OEMCryptoResult RenewalRoundTrip::LoadResponse(Session* session) { + // Write corpus for oemcrypto_load_renewal_fuzz. Fuzz script expects + // encrypted response from Renewal server as input corpus data. + // Data will be signed again explicitly by fuzzer script after mutations. + if (ShouldGenerateCorpus()) { + const std::string file_name = + GetFileName("oemcrypto_load_renewal_fuzz_seed_corpus"); + // Corpus for renewal response fuzzer should be in the format: + // OEMCrypto_Renewal_Response_Fuzz + license_renewal_response. + OEMCrypto_Renewal_Response_Fuzz renewal_response_fuzz; + renewal_response_fuzz.core_request = core_request_; + renewal_response_fuzz.renewal_duration_seconds = renewal_duration_seconds_; + AppendToFile(file_name, + reinterpret_cast(&renewal_response_fuzz), + sizeof(renewal_response_fuzz)); + AppendToFile(file_name, + reinterpret_cast(&encrypted_response_data_), + sizeof(encrypted_response_data_)); + } if (license_messages_->api_version() < kCoreMessagesAPI) { return OEMCrypto_RefreshKeys( session->session_id(), encrypted_response_.data(), @@ -1129,10 +1323,9 @@ void Session::TestDecryptResult(OEMCryptoResult expected_result, void Session::TestSelectExpired(unsigned int key_index) { if (global_features.api_version >= 13) { - OEMCryptoResult status = - OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id, - license().keys[key_index].key_id_length, - OEMCrypto_CipherMode_CTR); + OEMCryptoResult status = OEMCrypto_SelectKey( + session_id(), license().keys[key_index].key_id, + license().keys[key_index].key_id_length, OEMCrypto_CipherMode_CTR); // It is OK for SelectKey to succeed with an expired key, but if there is // an error, it must be OEMCrypto_ERROR_KEY_EXIRED. if (status != OEMCrypto_SUCCESS) { @@ -1200,9 +1393,9 @@ void Session::LoadOEMCert(bool verify_cert) { // TODO(fredgc): Verify cert is signed by Google. int result = X509_verify_cert(store_ctx.get()); - ASSERT_GE(0, result) << " OEM Cert not valid. " << - X509_verify_cert_error_string( - X509_STORE_CTX_get_error(store_ctx.get())); + ASSERT_GE(0, result) << " OEM Cert not valid. " + << X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get())); if (result == 0) { printf("Cert not verified: %s.\n", X509_verify_cert_error_string( @@ -1258,7 +1451,7 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, } if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, - const_cast(EVP_sha1())) != 1) { + const_cast(EVP_sha1())) != 1) { LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); goto err; } @@ -1307,18 +1500,17 @@ void Session::VerifyRSASignature(const vector& message, boringssl_ptr pkey(EVP_PKEY_new()); ASSERT_EQ(1, EVP_PKEY_set1_RSA(pkey.get(), public_rsa_)); - const bool ok = VerifyPSSSignature( - pkey.get(), message.data(), message.size(), signature, - signature_length); + const bool ok = + VerifyPSSSignature(pkey.get(), message.data(), message.size(), + signature, signature_length); EXPECT_TRUE(ok) << "PSS signature check failed."; } else if (padding_scheme == kSign_PKCS1_Block1) { vector padded_digest(signature_length); int size; // RSA_public_decrypt decrypts the signature, and then verifies that // it was padded with RSA PKCS1 padding. - size = RSA_public_decrypt( - signature_length, signature, padded_digest.data(), public_rsa_, - RSA_PKCS1_PADDING); + size = RSA_public_decrypt(signature_length, signature, padded_digest.data(), + public_rsa_, RSA_PKCS1_PADDING); EXPECT_GT(size, 0); padded_digest.resize(size); EXPECT_EQ(message, padded_digest); @@ -1384,16 +1576,14 @@ void Session::UpdateUsageEntry(std::vector* header_buffer) { void Session::LoadUsageEntry(uint32_t index, const vector& buffer) { usage_entry_number_ = index; encrypted_usage_entry_ = buffer; - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadUsageEntry( - session_id(), index, buffer.data(), buffer.size())); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(session_id(), index, buffer.data(), + buffer.size())); } void Session::MoveUsageEntry(uint32_t new_index, std::vector* header_buffer, OEMCryptoResult expect_result) { - ASSERT_NO_FATAL_FAILURE(open()); ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry()); ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index)); @@ -1405,8 +1595,7 @@ void Session::MoveUsageEntry(uint32_t new_index, } void Session::GenerateReport(const std::string& pst, - OEMCryptoResult expected_result, - Session* other) { + OEMCryptoResult expected_result, Session* other) { ASSERT_TRUE(open_); if (other) { // If other is specified, copy mac keys. key_deriver_ = other->key_deriver_; @@ -1442,14 +1631,13 @@ void Session::GenerateReport(const std::string& pst, void Session::VerifyPST(const Test_PST_Report& expected) { wvcdm::Unpacked_PST_Report computed = pst_report(); EXPECT_EQ(expected.status, computed.status()); - char* pst_ptr = reinterpret_cast(computed.pst()); + char* pst_ptr = reinterpret_cast(computed.pst()); std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length()); ASSERT_EQ(expected.pst, computed_pst); int64_t now = wvcdm::Clock().GetCurrentTime(); int64_t age = now - expected.time_created; // How old is this report. EXPECT_NEAR(expected.seconds_since_license_received + age, - computed.seconds_since_license_received(), - kTimeTolerance); + computed.seconds_since_license_received(), kTimeTolerance); // Decrypt times only valid on licenses that have been active. if (expected.status == kActive || expected.status == kInactiveUsed) { EXPECT_NEAR(expected.seconds_since_first_decrypt + age, @@ -1461,8 +1649,8 @@ void Session::VerifyPST(const Test_PST_Report& expected) { } std::vector signature(SHA_DIGEST_LENGTH); key_deriver_.ClientSignPstReport(pst_report_buffer_, &signature); - EXPECT_EQ(0, memcmp(computed.signature(), signature.data(), - SHA_DIGEST_LENGTH)); + EXPECT_EQ(0, + memcmp(computed.signature(), signature.data(), SHA_DIGEST_LENGTH)); } void Session::VerifyReport(Test_PST_Report expected, @@ -1484,4 +1672,41 @@ void Session::VerifyReport(Test_PST_Report expected, : 0; ASSERT_NO_FATAL_FAILURE(VerifyPST(expected)); } + +bool ConvertByteToValidBoolean(const bool* in) { + const char* buf = reinterpret_cast(in); + for (size_t i = 0; i < sizeof(bool); i++) { + if (buf[i]) { + return true; + } + } + return false; +} + +template +void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length, + vector& data) { + std::string file_name; + if (std::is_same::value) { + file_name = GetFileName("oemcrypto_license_request_fuzz_seed_corpus"); + } else if (std::is_same< + CoreRequest, + oemcrypto_core_message::ODK_ProvisioningRequest>::value) { + file_name = GetFileName("oemcrypto_provisioning_request_fuzz_seed_corpus"); + } else if (std::is_same::value) { + file_name = GetFileName("oemcrypto_renewal_request_fuzz_seed_corpus"); + } else { + LOGE("Invalid CoreRequest type while writing request api corups."); + } + // Corpus for request APIs should be signature_length + core_message_length + + // data pointer. + AppendToFile(file_name, reinterpret_cast(&signature_length), + sizeof(signature_length)); + AppendToFile(file_name, reinterpret_cast(&core_message_length), + sizeof(core_message_length)); + AppendToFile(file_name, reinterpret_cast(data.data()), + data.size()); +} } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index 54ee3187..a3773fd3 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -18,6 +18,7 @@ #include "odk.h" #include "oec_device_features.h" #include "oec_key_deriver.h" +#include "oemcrypto_fuzz_structs.h" #include "oemcrypto_types.h" #include "pst_report.h" @@ -32,6 +33,8 @@ void PrintTo(const vector& value, ostream* os); } // namespace std namespace wvoec { +// OEMCrypto Fuzzing: Set max signture length to 1mb. +const size_t MB = 1024 * 1024; // Make sure this is larger than kMaxKeysPerSession, in oemcrypto_test.cpp constexpr size_t kMaxNumKeys = 30; @@ -158,6 +161,9 @@ class RoundTrip { // Have OEMCrypto sign a request message and then verify the signature and the // core message. virtual void SignAndVerifyRequest(); + // Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid + // License/Provisioning/Renwal request data that can be serialized. + virtual void InjectFuzzedRequestData(uint8_t* data, size_t size); // Create a default |response_data| and |core_response|. virtual void CreateDefaultResponse() = 0; // Copy fields from |response_data| to |padded_response_data|, encrypting @@ -241,6 +247,11 @@ class ProvisioningRoundTrip void set_allowed_schemes(uint32_t allowed_schemes) { allowed_schemes_ = allowed_schemes; } + // Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid + // provisioning response data that can be parsed. Calculates signature for + // data generated by fuzzer, so that signature validation passes when parsing + // provisioning response. + void InjectFuzzedResponseData(const uint8_t* data, size_t size); protected: void VerifyRequestSignature(const vector& data, @@ -286,6 +297,18 @@ class LicenseRoundTrip license_type_(OEMCrypto_ContentLicense), request_hash_() {} void CreateDefaultResponse() override; + // Used for OEMCrypto Fuzzing: Function to inject fuzzed timer limits + // into timer_limits field from core_response. We need to fuzz timer + // limits in order to efficiently fuzz load renewal response API. + void InjectFuzzedTimerLimits(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data); + // Used for OEMCrypto Fuzzing: Function to convert fuzzer data to valid + // License response data that can be parsed. Calculates signature for data + // generated by fuzzer, so that signature validation passes when parsing + // license response. + void InjectFuzzedResponseData(const uint8_t* data, size_t size); + // Used for OEMCrypto Fuzzing: Convert boolean flags in parsed_license to + // valid bytes to avoid errors from msan. + void ConvertDataToValidBools(ODK_ParsedLicense* t); // Create a license with four keys. Each key is responsible for one of generic // encrypt (key 0), decrypt (key 1), sign (key 2) and verify (key 3). Each key // is allowed only one type of operation. @@ -386,6 +409,9 @@ class RenewalRoundTrip is_release_(false) {} void CreateDefaultResponse() override; void EncryptAndSignResponse() override; + void InjectFuzzedResponseData(OEMCrypto_Renewal_Response_Fuzz& fuzzed_data, + const uint8_t* renewal_response, + const size_t renewal_response_size); OEMCryptoResult LoadResponse() override { return LoadResponse(session_); } OEMCryptoResult LoadResponse(Session* session) override; uint64_t renewal_duration_seconds() const { @@ -557,15 +583,14 @@ class Session { // Verify the Usage Report. If any time is greater than 10 minutes, it is // assumed to be an absolute time, and time_since will be computed relative to // now. - void VerifyReport(Test_PST_Report report, - int64_t time_license_received = 0, + void VerifyReport(Test_PST_Report report, int64_t time_license_received = 0, int64_t time_first_decrypt = 0, int64_t time_last_decrypt = 0); // Create an entry in the old usage table based on the given report. - void CreateOldEntry(const Test_PST_Report &report); + void CreateOldEntry(const Test_PST_Report& report); // Create a new entry and copy the old entry into it. Then very the report // is right. - void CopyAndVerifyOldEntry(const Test_PST_Report &report, + void CopyAndVerifyOldEntry(const Test_PST_Report& report, std::vector* header_buffer); // The unencrypted license response or license renewal response. @@ -599,6 +624,13 @@ class Session { string pst_; }; +// Used for OEMCrypto Fuzzing: Convert byte to a valid boolean to avoid errors +// generated by msan. +bool ConvertByteToValidBoolean(const bool* in); +// Used for OEMCrypto Fuzzing: Generates corpus for request APIs. +template +void WriteRequestApiCorpus(size_t signature_length, size_t core_message_length, + vector& data); } // namespace wvoec #endif // CDM_OEC_SESSION_UTIL_H_ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp new file mode 100644 index 00000000..0478de69 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp @@ -0,0 +1,44 @@ +/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ +/* source code may only be used and distributed under the Widevine Master */ +/* License Agreement. */ +#include "oemcrypto_corpus_generator_helper.h" + +#include +#include + +namespace wvoec { +bool g_generate_corpus; + +void AppendToFile(const std::string& file_name, const char* message, + const size_t message_size) { + std::ofstream filebuf(file_name.c_str(), std::ios::app | std::ios::binary); + if (!filebuf) { + std::cout << "Cannot open file " << file_name.c_str() << std::endl; + } + filebuf.write(message, message_size); + filebuf.close(); +} + +void AppendSeparator(const std::string& file_name) { + std::ofstream filebuf(file_name.c_str(), std::ios::app | std::ios::binary); + if (!filebuf) { + std::cout << "Cannot open file " << file_name.c_str() << std::endl; + } + filebuf.write(reinterpret_cast(&kFuzzDataSeparator), + sizeof(kFuzzDataSeparator)); + filebuf.close(); +} + +std::string GetFileName(const char* directory) { + std::string file_name(PATH_TO_CORPUS); + file_name += directory; + file_name += "/"; + file_name += std::to_string(rand()); + return file_name; +} + +void SetGenerateCorpus(bool should_generate_corpus) { + g_generate_corpus = should_generate_corpus; +} +bool ShouldGenerateCorpus() { return g_generate_corpus; } +} // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h new file mode 100644 index 00000000..9086a0c1 --- /dev/null +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_corpus_generator_helper.h @@ -0,0 +1,30 @@ +/* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ +/* source code may only be used and distributed under the Widevine Master */ +/* License Agreement. */ +#ifndef CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ +#define CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ + +#define PATH_TO_CORPUS "./oemcrypto/test/fuzz_tests/corpus/" + +#include +#include +#include + +namespace wvoec { +const uint8_t kFuzzDataSeparator[] = {'-', '_', '^', '_'}; + +void AppendToFile(const std::string& file_name, const char* message, + const size_t message_size); + +// Function to append separator "-_^_" between contents of corpus file. +void AppendSeparator(const std::string& file_name); + +std::string GetFileName(const char* directory); + +void SetGenerateCorpus(bool should_generate_corpus); +// Output of this function decides if binary data needs to be written +// to corpus files or not. Controlled by --generate_corpus flag. +bool ShouldGenerateCorpus(); +} // namespace wvoec + +#endif // CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 2d845f48..f0b8900e 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -2,8 +2,29 @@ // source code may only be used and distributed under the Widevine Master // License Agreement. // -// OEMCrypto unit tests -// + +/** + * @mainpage OEMCrypto Unit Tests + * + * The OEMCrypto unit tests are designed to verify that an implementation of + * OEMCrypto is correctly supporting the OEMCrypto API. + * + * @defgroup basic Basic Functionality Tests + * Basic functionality tests. + * + * @defgroup license License Request Tests + * Test for requesting and loading licenses. + * + * @defgroup renewal License Renewal Tests + * Tests for renewing licenses. + * + * @defgroup decrypt Decrypt Tests + * Tests for decrypting content. + * + * @defgroup usage_table Usage Table Tests + * Tests that use the usage table. + */ + #include #include #include @@ -17,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +104,9 @@ constexpr size_t kBufferOverrunPadding = 16; // Resource tiers: constexpr size_t KiB = 1024; constexpr size_t MiB = 1024 * 1024; +// Huge input buffer length used for OEMCryptoMemory* tests. +constexpr size_t kHugeInputBufferLength = 100 * MiB; +constexpr bool kCheckStatus = true; // With OEMCrypto v15 and above, we have different resource requirements // depending on the resource rating reported by OEMCrypto. This function looks // up the required value for the specified resource for the target OEMCrypto @@ -93,6 +118,32 @@ T GetResourceValue(T (&resource_values)[N]) { return resource_values[global_features.resource_rating - 1]; } +// Used for testing oemcrypto APIs with huge buffers. +typedef const std::function oemcrypto_function; +// Function to test APIs that expect a buffer length as input +// by passing huge buffer lengths upto end_buffer_length and test that the API +// doesn't crash. +void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, + size_t start_buffer_length, + size_t end_buffer_length, + bool check_status) { + OEMCryptoResult sts = OEMCrypto_SUCCESS; + for (size_t buffer_length = start_buffer_length; + buffer_length < end_buffer_length && + (sts == OEMCrypto_SUCCESS || sts == OEMCrypto_ERROR_SHORT_BUFFER || + !check_status); + buffer_length *= 2) { + sts = f(buffer_length); + } +} + +// Function to test APIs that expect a buffer length as input +// by passing huge buffer lengths upto kHugeInputBufferLength and test that +// the API doesn't crash. +void TestHugeLengthDoesNotCrashAPI(oemcrypto_function f, bool check_status) { + TestHugeLengthDoesNotCrashAPI(f, 1, kHugeInputBufferLength, check_status); +} + // After API 16, we require 300 entries in the usage table. Before API 16, we // required 200. size_t RequiredUsageSize() { @@ -148,10 +199,14 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { } }; -// -// General tests. -// This test is first, becuase it might give an idea why other -// tests are failing when the device has the wrong keybox installed. +/// @addtogroup basic +/// @{ + +/** + * Verifies initialization and logs version information. + * This test is first, because it might give an idea why other + * tests are failing when the device has the wrong keybox installed. + */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = "OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01"; @@ -186,14 +241,18 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { ASSERT_LE(version, kCurrentAPI); } -// The resource rating is a number from 1 to 4. The first three levels were -// initially defined in API 15 and they were expanded in API 16. +/** + * The resource rating is a number from 1 to 4. The first three levels + * were initially defined in API 15 and they were expanded in API 16. + */ TEST_F(OEMCryptoClientTest, ResourceRatingAPI15) { ASSERT_GE(OEMCrypto_ResourceRatingTier(), 1u); ASSERT_LE(OEMCrypto_ResourceRatingTier(), 4u); } -// OEMCrypto must declare what type of provisioning scheme it uses. +/** + * OEMCrypto must declare what type of provisioning scheme it uses. + */ TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { OEMCrypto_ProvisioningMethod provisioning_method = OEMCrypto_GetProvisioningMethod(); @@ -228,10 +287,10 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) { OEMCrypto_HDCP_Capability current, maximum; sts = OEMCrypto_GetHDCPCapability(¤t, &maximum); ASSERT_EQ(OEMCrypto_SUCCESS, sts); - printf(" Current HDCP Capability: 0x%02x = %s.\n", current, - HDCPCapabilityAsString(current)); - printf(" Maximum HDCP Capability: 0x%02x = %s.\n", maximum, - HDCPCapabilityAsString(maximum)); + printf(" Current HDCP Capability: 0x%02x = %s.\n", + static_cast(current), HDCPCapabilityAsString(current)); + printf(" Maximum HDCP Capability: 0x%02x = %s.\n", + static_cast(maximum), HDCPCapabilityAsString(maximum)); } TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { @@ -285,6 +344,15 @@ TEST_F(OEMCryptoClientTest, NormalInitTermination) { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); } +// Test that set sandbox doesn't crash for a large sandbox id leangth. +TEST_F(OEMCryptoClientTest, OEMCryptoMemorySetSandboxForLargeSandboxIdLength) { + auto oemcrypto_function = [](size_t buffer_length) { + vector buffer(buffer_length); + return OEMCrypto_SetSandbox(buffer.data(), buffer.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + // // Session Tests // @@ -375,6 +443,17 @@ TEST_F(OEMCryptoClientTest, GetRandomLargeBuffer) { ASSERT_LE(count, 3); // P(count > 3) = 1/256^3 = 6e-8. } +// Verify that GetRandom doesn't crash for large input lengths. +TEST_F(OEMCryptoClientTest, OEMCryptoMemoryGetRandomForLargeBuffer) { + auto oemcrypto_function = [](size_t buffer_length) { + vector buffer(buffer_length); + // TODO(ellurubharath, fredgc): Need to re-evaluate this on a real device + // as GetRandom can be slow. + return OEMCrypto_GetRandom(buffer.data(), buffer.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + TEST_F(OEMCryptoClientTest, GenerateNonce) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -532,6 +611,33 @@ class OEMCryptoKeyboxTest : public OEMCryptoClientTest { } }; +// Test that OEMCrypto_InstallKeyboxOrOEMCert doesn't crash for large keybox. +TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryInstallKeyboxForHugeKeyboxBuffer) { + auto f = [](size_t keybox_length) { + vector keybox(keybox_length); + memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox)); + return OEMCrypto_InstallKeyboxOrOEMCert(keybox.data(), keybox.size()); + }; + // Starting at sizeof(kTestKeybox) as we are copying valid keybox + // at beginning of generated buffers. + TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength, + kCheckStatus); +} + +// This test verifies that load test key box doesn't crash for large +// buffer length. +TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBuffer) { + auto f = [](size_t keybox_length) { + vector keybox(keybox_length); + memcpy(keybox.data(), &kTestKeybox, sizeof(kTestKeybox)); + return OEMCrypto_LoadTestKeybox(keybox.data(), keybox.size()); + }; + // Starting at sizeof(kTestKeybox) as we are copying valid keybox + // at beginning of generated buffers. + TestHugeLengthDoesNotCrashAPI(f, sizeof(kTestKeybox), kHugeInputBufferLength, + kCheckStatus); +} + // This test is used to print the device ID to stdout. TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { OEMCryptoResult sts; @@ -543,6 +649,15 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetDeviceId) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } +TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetDeviceIdForHugeIdLength) { + auto oemcrypto_function = [](size_t input_length) { + size_t device_id_length = input_length; + vector device_id(device_id_length); + return OEMCrypto_GetDeviceID(device_id.data(), &device_id_length); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + TEST_F(OEMCryptoKeyboxTest, GetDeviceIdShortBuffer) { OEMCryptoResult sts; uint8_t dev_id[128]; @@ -570,11 +685,20 @@ TEST_F(OEMCryptoKeyboxTest, NormalGetKeyData) { sts = OEMCrypto_GetKeyData(key_data, &key_data_len); uint32_t* data = reinterpret_cast(key_data); - printf(" NormalGetKeyData: system_id = %d = 0x%04X, version=%d\n", + printf(" NormalGetKeyData: system_id = %u = 0x%04X, version=%u\n", htonl(data[1]), htonl(data[1]), htonl(data[0])); ASSERT_EQ(OEMCrypto_SUCCESS, sts); } +TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetKeyIdForLargeIdLength) { + auto oemcrypto_function = [](size_t input_length) { + size_t key_data_length = input_length; + vector key_data(key_data_length); + return OEMCrypto_GetKeyData(key_data.data(), &key_data_length); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + TEST_F(OEMCryptoKeyboxTest, GetKeyDataNullPointer) { OEMCryptoResult sts; uint8_t key_data[256]; @@ -606,6 +730,42 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) { enc_context.data(), enc_context.size())); } +TEST_F(OEMCryptoKeyboxTest, + OEMCryptoMemoryGenerateDerivedKeysForLargeMacContextLength) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector mac_context; + vector enc_context; + s.FillDefaultContext(&mac_context, &enc_context); + + auto oemcrypto_function = [&s, &mac_context, + &enc_context](size_t buffer_length) { + mac_context.resize(buffer_length); + return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(), + mac_context.size(), enc_context.data(), + enc_context.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + +TEST_F(OEMCryptoKeyboxTest, + OEMCryptoMemoryGenerateDerivedKeysForLargeEncContextLength) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + vector mac_context; + vector enc_context; + s.FillDefaultContext(&mac_context, &enc_context); + + auto oemcrypto_function = [&s, &mac_context, + &enc_context](size_t buffer_length) { + enc_context.resize(buffer_length); + return OEMCrypto_GenerateDerivedKeys(s.session_id(), mac_context.data(), + mac_context.size(), enc_context.data(), + enc_context.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + // This class is for tests that have an OEM Certificate instead of a keybox. class OEMCryptoProv30Test : public OEMCryptoClientTest {}; @@ -644,6 +804,11 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) { ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } +/// @} + +/// @addtogroup license +/// @{ + // This verifies that the OEM Certificate cannot be used for other RSA padding // schemes. Those schemes should only be used by cast receiver certificates. TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { @@ -711,6 +876,27 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } +TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForLargeCertLength) { + if (wrapped_rsa_key_.size() == 0) { + // If we don't have a wrapped key yet, create one. + // This wrapped key will be shared by all sessions in the test. + ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey()); + } + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + // Install the DRM Cert's RSA key. + ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey()); + + auto oemcrypto_function = [](size_t input_length) { + size_t public_cert_length = input_length; + vector public_cert(public_cert_length); + return OEMCrypto_GetOEMPublicCertificate(public_cert.data(), + &public_cert_length); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + // // AddKey Tests // @@ -836,6 +1022,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyNoNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } // Verify that a preloaded license may be loaded without first signing the @@ -849,6 +1036,8 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) { license_messages_.set_api_version(license_api_version_); } license_messages_.set_control(0); + // Notice that we do not call SignAndVerifyRequest -- we do not need a request + // in order to generate a response for a preloaded license. // The test code uses the core request to create the core response. license_messages_.core_request().api_major_version = ODK_MAJOR_VERSION; license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION; @@ -861,6 +1050,34 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) { ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2)); ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2)); + ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS)); +} + +// Verify that a license may be reloaded without a nonce, but with a nonzero +// rental duration. In order to start the rental clock, we sign a dummy license +// instead. +TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequestRentalDuration) { + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + license_messages_.set_control(0); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + // It is not recommended for a license without a nonce to have a nonzero + // rental duration. But there are content providers that have licenses with + // this policy. + license_messages_.core_response().timer_limits.rental_duration_seconds = + kDuration; + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + + // Load license in a different session, which did not create the request. + Session session2; + ASSERT_NO_FATAL_FAILURE(session2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2)); + // However, in order to start the rental clock, we have to sign something. So + // we will sign a dummy license request. + LicenseRoundTrip dummy_license(&session2); + ASSERT_NO_FATAL_FAILURE(dummy_license.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2)); + ASSERT_NO_FATAL_FAILURE(session2.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } // Verify that a license may be loaded with a nonce. @@ -870,6 +1087,7 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNonce) { ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + ASSERT_NO_FATAL_FAILURE(session_.TestDecryptCTR(true, OEMCrypto_SUCCESS)); } // Verify that a second license may not be loaded in a session. @@ -1336,6 +1554,10 @@ TEST_F(OEMCryptoLicenseTestAPI16, BadCoreHashAPI16) { ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); ASSERT_NE(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); } +/// @} + +/// @addtogroup decrypt +/// @{ // LoadKeys should fail if we try to load keys with no keys. TEST_P(OEMCryptoLicenseTest, LoadKeyNoKeys) { @@ -1520,6 +1742,58 @@ TEST_P(OEMCryptoLicenseTest, QueryKeyControl) { strlen(key_id), reinterpret_cast(&block), &size)); } +// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_id_length. +TEST_F(OEMCryptoLicenseTestAPI16, + OEMCryptoMemoryQueryKeyControlForHugeKeyIdLength) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + + OEMCrypto_SESSION session_id = session_.session_id(); + vector valid_key_id( + license_messages_.response_data().keys[0].key_id, + license_messages_.response_data().keys[0].key_id + kTestKeyIdMaxLength); + auto oemcrypto_function = [&session_id, + &valid_key_id](size_t additional_key_id_length) { + vector key_id(valid_key_id); + key_id.resize(valid_key_id.size() + additional_key_id_length); + KeyControlBlock block; + size_t size = sizeof(block); + return OEMCrypto_QueryKeyControl(session_id, key_id.data(), key_id.size(), + reinterpret_cast(&block), &size); + }; + // We do not want to stop as soon as API results in an error as it would + // return error on first iteration as key_id is invalid. + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + +// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_control_block +// length. +TEST_F(OEMCryptoLicenseTestAPI16, + OEMCryptoMemoryQueryKeyControlForHugeKeyControlBlockLength) { + ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + + OEMCrypto_SESSION session_id = session_.session_id(); + uint8_t* key_id = license_messages_.response_data().keys[0].key_id; + size_t key_id_length = + license_messages_.response_data().keys[0].key_id_length; + auto oemcrypto_function = [&session_id, &key_id, + &key_id_length](size_t buffer_length) { + size_t key_control_block_length = buffer_length; + vector key_control_block(key_control_block_length); + return OEMCrypto_QueryKeyControl(session_id, key_id, key_id_length, + key_control_block.data(), + &key_control_block_length); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + // If the device says it supports anti-rollback in the hardware, then it should // accept a key control block with the anti-rollback hardware bit set. // Otherwise, it should reject that key control block. @@ -1718,6 +1992,11 @@ TEST_P(OEMCryptoSessionTestDecryptWithHDCP, DecryptAPI09) { INSTANTIATE_TEST_CASE_P(TestHDCP, OEMCryptoSessionTestDecryptWithHDCP, Range(1, 6)); +/// @} + +/// @addtogroup renewal +/// @{ + // // Load, Refresh Keys Test // @@ -1903,6 +2182,30 @@ TEST_P(OEMCryptoLicenseTest, HashForbiddenAPI15) { OEMCrypto_GetHashErrorCode(session_.session_id(), &frame_number)); } +// This test verifies that OEMCrypto_SetDecryptHash doesn't crash for a very +// large hash buffer. +TEST_F(OEMCryptoLicenseTestAPI16, + OEMCryptoMemoryDecryptHashForLargeHashBuffer) { + uint32_t session_id = session_.session_id(); + auto f = [session_id](size_t hash_length) { + uint32_t frame_number = 1; + vector hash_buffer(hash_length); + return OEMCrypto_SetDecryptHash(session_id, frame_number, + hash_buffer.data(), hash_buffer.size()); + }; + TestHugeLengthDoesNotCrashAPI(f, sizeof(uint32_t), kHugeInputBufferLength, + kCheckStatus); +} + +// This test verifies OEMCrypto_SetDecryptHash for out of range frame number. +TEST_P(OEMCryptoLicenseTest, DecryptHashForOutOfRangeFrameNumber) { + uint32_t frame_number = 40; + uint32_t hash = 42; + ASSERT_NO_FATAL_FAILURE(OEMCrypto_SetDecryptHash( + session_.session_id(), frame_number, + reinterpret_cast(&hash), sizeof(hash))); +} + // // Decrypt Tests -- these test Decrypt CTR mode only. // @@ -2582,18 +2885,6 @@ TEST_P(OEMCryptoLicenseTest, DecryptSecureToClear) { session_.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } -// If analog is forbidden, then decrypt to a clear buffer should be forbidden. -TEST_P(OEMCryptoLicenseTest, DecryptNoAnalogToClearAPI13) { - ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); - ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); - license_messages_.set_control(wvoec::kControlDisableAnalogOutput); - ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); - ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); - ASSERT_NO_FATAL_FAILURE( - session_.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); -} - // Test that key duration is honored. TEST_P(OEMCryptoLicenseTest, KeyDuration) { ASSERT_NO_FATAL_FAILURE(session_.GenerateNonce()); @@ -3087,6 +3378,50 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { enc_context.data(), enc_context.size())); } +TEST_F(OEMCryptoUsesCertificate, + OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) { + vector session_key; + vector enc_session_key; + ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(), + encoded_rsa_key_.size())); + ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + vector mac_context; + vector enc_context; + session_.FillDefaultContext(&mac_context, &enc_context); + OEMCrypto_SESSION session_id = session_.session_id(); + auto oemcrypto_function = [&session_id, &enc_context, &mac_context, + &enc_session_key](size_t buffer_length) { + mac_context.resize(buffer_length); + return OEMCrypto_DeriveKeysFromSessionKey( + session_id, enc_session_key.data(), enc_session_key.size(), + mac_context.data(), mac_context.size(), enc_context.data(), + enc_context.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + +TEST_F(OEMCryptoUsesCertificate, + OEMCryptoMemoryDeriveKeysFromSessionKeyForLargeEncContext) { + vector session_key; + vector enc_session_key; + ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(encoded_rsa_key_.data(), + encoded_rsa_key_.size())); + ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); + vector mac_context; + vector enc_context; + session_.FillDefaultContext(&mac_context, &enc_context); + OEMCrypto_SESSION session_id = session_.session_id(); + auto oemcrypto_function = [&session_id, &enc_context, &mac_context, + &enc_session_key](size_t buffer_length) { + enc_context.resize(buffer_length); + return OEMCrypto_DeriveKeysFromSessionKey( + session_id, enc_session_key.data(), enc_session_key.size(), + mac_context.data(), mac_context.size(), enc_context.data(), + enc_context.size()); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); +} + // This test attempts to use alternate algorithms for loaded device certs. class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { protected: @@ -4369,6 +4704,17 @@ TEST_P(OEMCryptoGenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { ASSERT_EQ(expected_encrypted, buffer); } +TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemorySelectKeyForHugeKeyIdLength) { + EncryptAndLoadKeys(); + OEMCrypto_SESSION session_id = session_.session_id(); + auto oemcrypto_function = [session_id](size_t key_id_length) { + vector key_id(key_id_length); + return OEMCrypto_SelectKey(session_id, key_id.data(), key_id.size(), + OEMCrypto_CipherMode_CTR); + }; + TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); +} + // Test Generic_Decrypt works correctly. TEST_P(OEMCryptoGenericCryptoTest, GenericKeyDecrypt) { EncryptAndLoadKeys(); @@ -4833,6 +5179,11 @@ INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoTest, INSTANTIATE_TEST_CASE_P(TestAll, OEMCryptoGenericCryptoKeyIdLengthTest, Range(kCurrentAPI - 1, kCurrentAPI + 1)); +/// @} + +/// @addtogroup usage_table +/// @{ + // Test usage table functionality. class LicenseWithUsageEntry { public: @@ -6186,9 +6537,7 @@ TEST_P(OEMCryptoUsageTableTest, VerifyUsageTimes) { // on a device that allows an application to set the clock. class OEMCryptoUsageTableTestWallClock : public OEMCryptoUsageTableTest { public: - void SetUp() override { - OEMCryptoUsageTableTest::SetUp(); - } + void SetUp() override { OEMCryptoUsageTableTest::SetUp(); } void TearDown() override { wvcdm::TestSleep::ResetRollback(); @@ -6332,4 +6681,5 @@ INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableDefragTest, INSTANTIATE_TEST_CASE_P(TestAPI16, OEMCryptoUsageTableTestWallClock, Values(kCurrentAPI)); +/// @} } // namespace wvoec diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp index 1cf4a598..5815fd4d 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test_main.cpp @@ -4,6 +4,7 @@ #include "OEMCryptoCENC.h" #include "log.h" #include "oec_device_features.h" +#include "oemcrypto_corpus_generator_helper.h" #include "test_sleep.h" static void acknowledge_cast() { @@ -23,6 +24,9 @@ int main(int argc, char** argv) { // Skip the first element, which is the program name. const std::vector args(argv + 1, argv + argc); for (const std::string& arg : args) { + if (arg == "--generate_corpus") { + wvoec::SetGenerateCorpus(true); + } if (arg == "--verbose" || arg == "-v") { ++verbosity; } else if (arg == "--cast") {