From 6be2236c8c99999b30553bb7b651c6380b4b6bf7 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Sat, 14 Apr 2018 00:14:24 +0800 Subject: [PATCH 01/16] added code for widevine Pssh --- deltaGenericKeyProvider.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 62d4d90..8499240 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -219,10 +219,22 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem break case _WIDEVINE_SYSTEM_ID: // TODO: implement proper (HTTP) error handling - data, err := generateProtoBuf([]byte(drmSystem.Kid), []byte(id), _WIDEVINE_PROVIDER, _WIDEVINE_TRACKTYPE) - if err != nil { - log.Fatal(err) - } + // data, err := generateProtoBuf([]byte(drmSystem.Kid), []byte(id), _WIDEVINE_PROVIDER, _WIDEVINE_TRACKTYPE) + // if err != nil { + // log.Fatal(err) + // } + + data := []byte{ + 0x00, 0x00, 0x00, 0x44, 0x70, 0x73, 0x73, 0x68, // BMFF box header (68 bytes, 'pssh') + 0x01, 0x00, 0x00, 0x00, // Full box header (version = 1, flags = 0) + 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // SystemID + 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, + 0x00, 0x00, 0x00, 0x02, // KID_count (2) + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // First KID ("0123456789012345") + 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, // Second KID ("ABCDEFGHIJKLMNOP") + 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x00, 0x00, 0x00, 0x00} // Size of Data (0) resDrmSystems[i].Pssh = encode.BytesToBase64(data) break From 54b89fb64132d3f508683ec181ac4f972653dc0b Mon Sep 17 00:00:00 2001 From: Saravanan Date: Sun, 15 Apr 2018 16:45:35 +0800 Subject: [PATCH 02/16] updated pssh with widvine specific values --- deltaGenericKeyProvider.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 8499240..e4298be 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -227,13 +227,13 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem data := []byte{ 0x00, 0x00, 0x00, 0x44, 0x70, 0x73, 0x73, 0x68, // BMFF box header (68 bytes, 'pssh') 0x01, 0x00, 0x00, 0x00, // Full box header (version = 1, flags = 0) - 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // SystemID - 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, - 0x00, 0x00, 0x00, 0x02, // KID_count (2) - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // First KID ("0123456789012345") - 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, // Second KID ("ABCDEFGHIJKLMNOP") - 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + // 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // SystemID + // 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, + 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, + 0x00, 0x00, 0x00, 0x01, // KID_count (1) + 0x00, 0x00, 0xbe, 0xcb, 0xba, 0xdd, 0x44, 0x26, + 0x11, 0x56, 0x8e, 0x4e, 0xfd, 0x58, 0x27, 0x70, 0x00, 0x00, 0x00, 0x00} // Size of Data (0) resDrmSystems[i].Pssh = encode.BytesToBase64(data) From 74b88a72b53f8ac6d0100c230b4053baee02dbc2 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Sun, 15 Apr 2018 16:54:16 +0800 Subject: [PATCH 03/16] fixed key id --- deltaGenericKeyProvider.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index e4298be..ac38d71 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -232,8 +232,8 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x01, // KID_count (1) - 0x00, 0x00, 0xbe, 0xcb, 0xba, 0xdd, 0x44, 0x26, - 0x11, 0x56, 0x8e, 0x4e, 0xfd, 0x58, 0x27, 0x70, + 0x62, 0x4f, 0xaf, 0xb2, 0xee, 0xb7, 0x51, 0x09, + 0x84, 0x55, 0xa3, 0x93, 0xbf, 0x56, 0x09, 0xdc 0x00, 0x00, 0x00, 0x00} // Size of Data (0) resDrmSystems[i].Pssh = encode.BytesToBase64(data) From d8922eb6b73749dd80de3be37252038c51f75d54 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Sun, 15 Apr 2018 16:55:26 +0800 Subject: [PATCH 04/16] fixed syntax error --- deltaGenericKeyProvider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index ac38d71..eef2811 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -233,7 +233,7 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x01, // KID_count (1) 0x62, 0x4f, 0xaf, 0xb2, 0xee, 0xb7, 0x51, 0x09, - 0x84, 0x55, 0xa3, 0x93, 0xbf, 0x56, 0x09, 0xdc + 0x84, 0x55, 0xa3, 0x93, 0xbf, 0x56, 0x09, 0xdc, 0x00, 0x00, 0x00, 0x00} // Size of Data (0) resDrmSystems[i].Pssh = encode.BytesToBase64(data) From c4970b6ae4c012a39e88e5d47470f370e5393d81 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Sun, 15 Apr 2018 18:44:40 +0800 Subject: [PATCH 05/16] new content id for widevine --- deltaGenericKeyProvider.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index eef2811..478ea76 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -219,10 +219,6 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem break case _WIDEVINE_SYSTEM_ID: // TODO: implement proper (HTTP) error handling - // data, err := generateProtoBuf([]byte(drmSystem.Kid), []byte(id), _WIDEVINE_PROVIDER, _WIDEVINE_TRACKTYPE) - // if err != nil { - // log.Fatal(err) - // } data := []byte{ 0x00, 0x00, 0x00, 0x44, 0x70, 0x73, 0x73, 0x68, // BMFF box header (68 bytes, 'pssh') @@ -232,8 +228,8 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x01, // KID_count (1) - 0x62, 0x4f, 0xaf, 0xb2, 0xee, 0xb7, 0x51, 0x09, - 0x84, 0x55, 0xa3, 0x93, 0xbf, 0x56, 0x09, 0xdc, + 0xee, 0xe4, 0x05, 0xed, 0xde, 0xa6, 0x4f, 0x37, + 0x8e, 0x51, 0xec, 0x16, 0x7e, 0xca, 0x8d, 0x33, 0x00, 0x00, 0x00, 0x00} // Size of Data (0) resDrmSystems[i].Pssh = encode.BytesToBase64(data) From 9c6650635ac4d59561cf2f5a694339151d7386c0 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 16 Apr 2018 15:12:39 +0800 Subject: [PATCH 06/16] fixed pssh data --- deltaGenericKeyProvider.go | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 478ea76..ad13596 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -220,19 +220,20 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem case _WIDEVINE_SYSTEM_ID: // TODO: implement proper (HTTP) error handling - data := []byte{ - 0x00, 0x00, 0x00, 0x44, 0x70, 0x73, 0x73, 0x68, // BMFF box header (68 bytes, 'pssh') + data, _ := generateProtoBuf(encode.HexStringToBin("eee405eddea64f378e51ec167eca8d33"), encode.HexStringToBin("74657374313233"), "widevine_test", "SD") + pssh := []byte{ + 0x00, 0x00, 0x00, 0x5e, 0x70, 0x73, 0x73, 0x68, // BMFF box header (68 bytes, 'pssh') 0x01, 0x00, 0x00, 0x00, // Full box header (version = 1, flags = 0) - // 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // SystemID - // 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, - 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, // Widevine SystemID 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, 0x00, 0x00, 0x00, 0x01, // KID_count (1) - 0xee, 0xe4, 0x05, 0xed, 0xde, 0xa6, 0x4f, 0x37, + 0xee, 0xe4, 0x05, 0xed, 0xde, 0xa6, 0x4f, 0x37, // Key Id 0x8e, 0x51, 0xec, 0x16, 0x7e, 0xca, 0x8d, 0x33, - 0x00, 0x00, 0x00, 0x00} // Size of Data (0) + 0x00, 0x00, 0x00, 0x2a} - resDrmSystems[i].Pssh = encode.BytesToBase64(data) + pssh = append(pssh, data...) + + resDrmSystems[i].Pssh = encode.BytesToBase64(pssh) break } resDrmSystems[i].Kid = drmSystems[i].Kid @@ -260,16 +261,11 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem func generateProtoBuf(keyId []byte, contentId []byte, provider string, trackType string) ([]byte, error) { key_id := [][]byte{keyId} - algorithm_value := pb.WidevineCencHeader_AESCTR - policy := string("") pssh := &pb.WidevineCencHeader{ - Algorithm: &algorithm_value, - KeyId: key_id, - Provider: &provider, - ContentId: contentId, - TrackTypeDeprecated: &trackType, - Policy: &policy} + KeyId: key_id, + Provider: &provider, + ContentId: contentId} data, err := proto.Marshal(pssh) if err != nil { From 8dd699b24413fbc1b8942921863e961d8414b631 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 16 Apr 2018 22:30:29 +0800 Subject: [PATCH 07/16] added dynamic mp4 pssh creation --- deltaGenericKeyProvider.go | 80 +++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index ad13596..1dd08e8 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -3,11 +3,13 @@ package main import ( pb "WidevineCencHeader" "encoding/xml" + "errors" "helper/encode" "io/ioutil" "log" "middleware" "net/http" + "strings" "github.com/golang/protobuf/proto" ) @@ -220,20 +222,20 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem case _WIDEVINE_SYSTEM_ID: // TODO: implement proper (HTTP) error handling - data, _ := generateProtoBuf(encode.HexStringToBin("eee405eddea64f378e51ec167eca8d33"), encode.HexStringToBin("74657374313233"), "widevine_test", "SD") - pssh := []byte{ - 0x00, 0x00, 0x00, 0x5e, 0x70, 0x73, 0x73, 0x68, // BMFF box header (68 bytes, 'pssh') - 0x01, 0x00, 0x00, 0x00, // Full box header (version = 1, flags = 0) - 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, // Widevine SystemID - 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed, - 0x00, 0x00, 0x00, 0x01, // KID_count (1) - 0xee, 0xe4, 0x05, 0xed, 0xde, 0xa6, 0x4f, 0x37, // Key Id - 0x8e, 0x51, 0xec, 0x16, 0x7e, 0xca, 0x8d, 0x33, - 0x00, 0x00, 0x00, 0x2a} + contentIdInBin := []byte(id) + contentKeyInBin := encode.HexStringToBin(strings.Replace(contentKeys[0].Kid, "-", "", -1)) - pssh = append(pssh, data...) + widevinePssh, err := generateWidevinePssh(contentKeyInBin, contentIdInBin, "widevine_test", "SD") + if err != nil { + return + } - resDrmSystems[i].Pssh = encode.BytesToBase64(pssh) + mp4Pssh, err := generateMp4Pssh(contentKeyInBin, strings.Replace(_WIDEVINE_SYSTEM_ID, "-", "", -1), widevinePssh) + if err != nil { + return + } + + resDrmSystems[i].Pssh = encode.BytesToBase64(mp4Pssh) break } resDrmSystems[i].Kid = drmSystems[i].Kid @@ -258,7 +260,7 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem return spekeResponse, nil } -func generateProtoBuf(keyId []byte, contentId []byte, provider string, trackType string) ([]byte, error) { +func generateWidevinePssh(keyId []byte, contentId []byte, provider string, trackType string) ([]byte, error) { key_id := [][]byte{keyId} @@ -276,3 +278,55 @@ func generateProtoBuf(keyId []byte, contentId []byte, provider string, trackType return data, nil } + +// generateMp4Pssh creates an version 1 MP4 Pssh per the https://www.w3.org/TR/eme-initdata-cenc/#common-system +// NOTE: this function currently does not support multiple key id. +func generateMp4Pssh(keyIdInBin []byte, systemId string, drmPsshInBin []byte) ([]byte, error) { + + boxSizeInBoxHeader := make([]byte, 4) + psshInBoxHeader := []byte{0x70, 0x73, 0x73, 0x68} // 'pssh' + versionAndFlags := []byte{0x01, 0x00, 0x00, 0x00} // Full box header (version = 1, flags = 0) + systemIdInBin := encode.HexStringToBin(systemId) + + keyIdCountInBin := make([]byte, 4) + // TODO: enable multiple key PSSH + keyCount := 1 + // Convert the key count to 4 bytes + keyIdCountInBin[0] = byte(keyCount >> 24) + keyIdCountInBin[1] = byte(keyCount >> 16) + keyIdCountInBin[2] = byte(keyCount >> 8) + keyIdCountInBin[3] = byte(keyCount) + + // ensure the key id is 16 bytes else return error + if len(keyIdInBin) != 16 { + err := errors.New("Invalid key id") + return nil, err + } + + sizeOfDrmPssh := len(drmPsshInBin) + sizeOfDrmPsshInBin := make([]byte, 4) + // Convert the drm pssh size to 4 bytes + sizeOfDrmPsshInBin[0] = byte(sizeOfDrmPssh >> 24) + sizeOfDrmPsshInBin[1] = byte(sizeOfDrmPssh >> 16) + sizeOfDrmPsshInBin[2] = byte(sizeOfDrmPssh >> 8) + sizeOfDrmPsshInBin[3] = byte(sizeOfDrmPssh) + + pssh := []byte{} + pssh = append(pssh, boxSizeInBoxHeader...) + pssh = append(pssh, psshInBoxHeader...) + pssh = append(pssh, versionAndFlags...) + pssh = append(pssh, systemIdInBin...) + pssh = append(pssh, keyIdCountInBin...) + pssh = append(pssh, keyIdInBin...) + pssh = append(pssh, sizeOfDrmPsshInBin...) + pssh = append(pssh, drmPsshInBin...) + + sizeOfPssh := len(pssh) + // Convert the drm pssh size to 4 bytes + pssh[0] = byte(sizeOfPssh >> 24) + pssh[1] = byte(sizeOfPssh >> 16) + pssh[2] = byte(sizeOfPssh >> 8) + pssh[3] = byte(sizeOfPssh) + + return pssh, nil +} From 01c635db41d9bfcd2b9497c99b0032bc272f5e28 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 16 Apr 2018 22:30:46 +0800 Subject: [PATCH 08/16] added unit test for mp4pssh generator --- deltaGenericKeyProvider_test.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/deltaGenericKeyProvider_test.go b/deltaGenericKeyProvider_test.go index 3c06cea..39325da 100644 --- a/deltaGenericKeyProvider_test.go +++ b/deltaGenericKeyProvider_test.go @@ -5,14 +5,26 @@ import ( "testing" ) -func TestGenerateProtoBuf(t *testing.T) { +func TestGenerateWidevinePssh(t *testing.T) { - data, err := generateProtoBuf(encode.HexStringToBin("79221c6e568d6dbec38e2eac02d299cd"), - []byte("key-id:eSIcblaNbb7Dji6sAtKZzQ=="), - "widevine_test", - "SD") + contentIdInBin := []byte("test123") + keyIdInBin := encode.HexStringToBin("eee405eddea64f378e51ec167eca8d33") + data, err := generateWidevinePssh(keyIdInBin, contentIdInBin, "widevine_test", "SD") - if encode.BytesToBase64(data) != "CAESEHkiHG5WjW2+w44urALSmc0aDXdpZGV2aW5lX3Rlc3QiH2tleS1pZDplU0ljYmxhTmJiN0RqaTZzQXRLWnpRPT0qAlNEMgA=" || err != nil { + t.Log(encode.BytesToBase64(data)) + + if encode.BytesToBase64(data) != "EhDu5AXt3qZPN45R7BZ+yo0zGg13aWRldmluZV90ZXN0Igd0ZXN0MTIz" || err != nil { + t.FailNow() + } +} + +func TestGenerateMp4Pssh(t *testing.T) { + contentIdInBin := []byte("test123") + keyIdInBin := encode.HexStringToBin("eee405eddea64f378e51ec167eca8d33") + widevinePssh, _ := generateWidevinePssh(keyIdInBin, contentIdInBin, "widevine_test", "SD") + + mp4Pssh, err := generateMp4Pssh(keyIdInBin, "edef8ba979d64acea3c827dcd51d21ed", widevinePssh) + if encode.BytesToBase64(mp4Pssh) != "AAAAXnBzc2gBAAAA7e+LqXnWSs6jyCfc1R0h7QAAAAHu5AXt3qZPN45R7BZ+yo0zAAAAKhIQ7uQF7d6mTzeOUewWfsqNMxoNd2lkZXZpbmVfdGVzdCIHdGVzdDEyMw==" || err != nil { t.FailNow() } } From 43aa6c146e8feee2dae6eef983af19420eee14e7 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 16 Apr 2018 23:18:07 +0800 Subject: [PATCH 09/16] added support for multiple key ids --- deltaGenericKeyProvider.go | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 1dd08e8..cf62d83 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -223,7 +223,11 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem // TODO: implement proper (HTTP) error handling contentIdInBin := []byte(id) - contentKeyInBin := encode.HexStringToBin(strings.Replace(contentKeys[0].Kid, "-", "", -1)) + contentKeyInBin := [][]byte{} + + for i, contentKey := range contentKeys { + contentKeyInBin[i] = encode.HexStringToBin(strings.Replace(contentKey.Kid, "-", "", -1)) + } widevinePssh, err := generateWidevinePssh(contentKeyInBin, contentIdInBin, "widevine_test", "SD") if err != nil { @@ -260,12 +264,10 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem return spekeResponse, nil } -func generateWidevinePssh(keyId []byte, contentId []byte, provider string, trackType string) ([]byte, error) { - - key_id := [][]byte{keyId} +func generateWidevinePssh(keyId [][]byte, contentId []byte, provider string, trackType string) ([]byte, error) { pssh := &pb.WidevineCencHeader{ - KeyId: key_id, + KeyId: keyId, Provider: &provider, ContentId: contentId} @@ -281,7 +283,7 @@ func generateWidevinePssh(keyId []byte, contentId []byte, provider string, track // generateMp4Pssh creates an version 1 MP4 Pssh per the https://www.w3.org/TR/eme-initdata-cenc/#common-system // NOTE: this function currently does not support multiple key id. -func generateMp4Pssh(keyIdInBin []byte, systemId string, drmPsshInBin []byte) ([]byte, error) { +func generateMp4Pssh(keyIdsInBin [][]byte, systemId string, drmPsshInBin []byte) ([]byte, error) { boxSizeInBoxHeader := make([]byte, 4) psshInBoxHeader := []byte{0x70, 0x73, 0x73, 0x68} // 'pssh' @@ -290,7 +292,7 @@ func generateMp4Pssh(keyIdInBin []byte, systemId string, drmPsshInBin []byte) ([ keyIdCountInBin := make([]byte, 4) // TODO: enable multiple key PSSH - keyCount := 1 + keyCount := len(keyIdsInBin) // Convert the key count to 4 bytes keyIdCountInBin[0] = byte(keyCount >> 24) keyIdCountInBin[1] = byte(keyCount >> 16) @@ -298,9 +300,11 @@ func generateMp4Pssh(keyIdInBin []byte, systemId string, drmPsshInBin []byte) ([ keyIdCountInBin[3] = byte(keyCount) // ensure the key id is 16 bytes else return error - if len(keyIdInBin) != 16 { - err := errors.New("Invalid key id") - return nil, err + for _, keyIdInBin := range keyIdsInBin { + if len(keyIdInBin) != 16 { + err := errors.New("Invalid key id") + return nil, err + } } sizeOfDrmPssh := len(drmPsshInBin) @@ -317,7 +321,9 @@ func generateMp4Pssh(keyIdInBin []byte, systemId string, drmPsshInBin []byte) ([ pssh = append(pssh, versionAndFlags...) pssh = append(pssh, systemIdInBin...) pssh = append(pssh, keyIdCountInBin...) - pssh = append(pssh, keyIdInBin...) + for _, keyIdInBin := range keyIdsInBin { + pssh = append(pssh, keyIdInBin...) + } pssh = append(pssh, sizeOfDrmPsshInBin...) pssh = append(pssh, drmPsshInBin...) From ad4cab083fddb749e4ad376e4819671b9e2d590d Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 16 Apr 2018 23:18:25 +0800 Subject: [PATCH 10/16] updated unit tests for multiple kids --- deltaGenericKeyProvider_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deltaGenericKeyProvider_test.go b/deltaGenericKeyProvider_test.go index 39325da..b1cd4a1 100644 --- a/deltaGenericKeyProvider_test.go +++ b/deltaGenericKeyProvider_test.go @@ -9,7 +9,7 @@ func TestGenerateWidevinePssh(t *testing.T) { contentIdInBin := []byte("test123") keyIdInBin := encode.HexStringToBin("eee405eddea64f378e51ec167eca8d33") - data, err := generateWidevinePssh(keyIdInBin, contentIdInBin, "widevine_test", "SD") + data, err := generateWidevinePssh([][]byte{keyIdInBin}, contentIdInBin, "widevine_test", "SD") t.Log(encode.BytesToBase64(data)) @@ -21,9 +21,9 @@ func TestGenerateWidevinePssh(t *testing.T) { func TestGenerateMp4Pssh(t *testing.T) { contentIdInBin := []byte("test123") keyIdInBin := encode.HexStringToBin("eee405eddea64f378e51ec167eca8d33") - widevinePssh, _ := generateWidevinePssh(keyIdInBin, contentIdInBin, "widevine_test", "SD") + widevinePssh, _ := generateWidevinePssh([][]byte{keyIdInBin}, contentIdInBin, "widevine_test", "SD") - mp4Pssh, err := generateMp4Pssh(keyIdInBin, "edef8ba979d64acea3c827dcd51d21ed", widevinePssh) + mp4Pssh, err := generateMp4Pssh([][]byte{keyIdInBin}, "edef8ba979d64acea3c827dcd51d21ed", widevinePssh) if encode.BytesToBase64(mp4Pssh) != "AAAAXnBzc2gBAAAA7e+LqXnWSs6jyCfc1R0h7QAAAAHu5AXt3qZPN45R7BZ+yo0zAAAAKhIQ7uQF7d6mTzeOUewWfsqNMxoNd2lkZXZpbmVfdGVzdCIHdGVzdDEyMw==" || err != nil { t.FailNow() } From f9461ed9b97fd956bb85ccf638b1dda7de8b690b Mon Sep 17 00:00:00 2001 From: saravanans Date: Mon, 16 Apr 2018 15:52:34 +0000 Subject: [PATCH 11/16] fixed memory bug on content key array --- deltaGenericKeyProvider.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index cf62d83..366ca63 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -10,7 +10,7 @@ import ( "middleware" "net/http" "strings" - + "net/http/httputil" "github.com/golang/protobuf/proto" ) @@ -99,7 +99,7 @@ func getKeyAndIv(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // TURN THIS ON/OFF TO ENABLE/DISABLE HTTP DEBUGGING - /* + dump, err := httputil.DumpRequest(r, true) if err != nil { log.Fatalln(err) @@ -108,7 +108,7 @@ func getKeyAndIv(next http.Handler) http.Handler { } log.Printf("%q", dump) - */ + next.ServeHTTP(w, r) }) @@ -205,9 +205,9 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem // Now we set DRM specific data statically // Ideally we'll want to pull this of a config - len := len(drmSystems) - resDrmSystems := make([]DRMSystemType, len) - sem := make(chan empty, len) // semaphore pattern + length := len(drmSystems) + resDrmSystems := make([]DRMSystemType, length) + sem := make(chan empty, length) // semaphore pattern // Here we use the semaphore pattern to parallelize the response for each drm system for i, drmSystem := range drmSystems { @@ -223,7 +223,7 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem // TODO: implement proper (HTTP) error handling contentIdInBin := []byte(id) - contentKeyInBin := [][]byte{} + contentKeyInBin := make([][]byte, len(contentKeys)) for i, contentKey := range contentKeys { contentKeyInBin[i] = encode.HexStringToBin(strings.Replace(contentKey.Kid, "-", "", -1)) @@ -249,7 +249,7 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem }(i, drmSystem) } // wait for goroutines to finish - for i := 0; i < len; i++ { + for i := 0; i < length; i++ { <-sem } From 1d921002d86afc448fc450b9d17d14a9973b74b4 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Fri, 20 Apr 2018 00:08:36 +0800 Subject: [PATCH 12/16] new speke endpoint to write keys to mpx key ds --- deltaGenericKeyProvider.go | 341 +++++++++++++++++++++++++++++++++++-- 1 file changed, 328 insertions(+), 13 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 366ca63..5ff3894 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -2,6 +2,8 @@ package main import ( pb "WidevineCencHeader" + "bytes" + "encoding/json" "encoding/xml" "errors" "helper/encode" @@ -9,8 +11,10 @@ import ( "log" "middleware" "net/http" - "strings" "net/http/httputil" + "net/url" + "strings" + "github.com/golang/protobuf/proto" ) @@ -69,8 +73,32 @@ type DRMSystemType struct { ProtectionHeader string `xml:"speke:ProtectionHeader,omitempty"` } +type ResolveMpxUrlType struct { + ResolvedUrl string `json:"resolveUrlByAccountResponse,attr"` +} + +type MpxKeyDsType struct { + Id string `json:"id,attr,omitempty"` + Title string `json:"title,attr"` + Context string `json:"context,attr"` + OwnerId string `json:"ownerId,attr"` + Algorithm string `json:"algorithm,attr"` + Kid string `json:"kid,attr"` + Secret string `json:"secret,attr"` + Size int `json:"size,attr"` + Target string `json:"target,attr"` + Type string `json:"type,attr"` +} + +type MpxKeyDsArrayType struct { + Entries []MpxKeyDsType `json:"entries,attr"` +} + type empty struct{} +var _key string = _KEY +var _iv string = _IV + func main() { startServer() } @@ -84,7 +112,11 @@ func startServer() { middleware.ResourceType{ Path: "/speke/v1.0/copyProtection", Method: "POST", - Handler: getKeyAndIv(middleware.IsRequestValid(sendSpekeResponse(http.HandlerFunc(final))))}} + Handler: getKeyAndIv(middleware.IsRequestValid(sendSpekeResponse(http.HandlerFunc(final))))}, + middleware.ResourceType{ + Path: "/speke/v1.0/copyProtectionWithMpx", + Method: "POST", + Handler: getKeyAndIv(middleware.IsRequestValid(setKeysOnMpxKeyDs(sendSpekeResponse(http.HandlerFunc(final)))))}} config := middleware.ConfigType{Port: 8080, Path: "/delta", Resources: resource} @@ -99,16 +131,15 @@ func getKeyAndIv(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // TURN THIS ON/OFF TO ENABLE/DISABLE HTTP DEBUGGING - - dump, err := httputil.DumpRequest(r, true) - if err != nil { - log.Fatalln(err) - message, status := middleware.GetErrorResponse(500, "Server unable to read body.") - http.Error(w, message, status) - } - log.Printf("%q", dump) - + dump, err := httputil.DumpRequest(r, true) + if err != nil { + log.Fatalln(err) + message, status := middleware.GetErrorResponse(500, "Server unable to read body.") + http.Error(w, message, status) + } + + log.Printf("%q", dump) next.ServeHTTP(w, r) }) @@ -132,6 +163,97 @@ func sendGenericResponse(next http.Handler) http.Handler { }) } +func setKeysOnMpxKeyDs(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Println("Reading request params...") + q := r.URL.Query() + mpxToken := q.Get("token") + mpxAccountId := q.Get("accountId") + if q.Get("key") != "" { + _key = q.Get("key") + // TODO: VALIDATE THE KEY VALUE + } else { + log.Printf(" Key param not specified. Using default [%s]", _key) + } + if q.Get("iv") != "" { + _iv = q.Get("iv") + log.Printf(" IV param not specified. Using default [%s]. Note this IV is not used for Widevine & Playready", _iv) + } + + log.Println("Reading request params... DONE") + + log.Println("Validating request params...") + if mpxAccountId != "" && mpxToken == "" { + err := errors.New("mpxAccountId specified w/o mpxToken") + log.Printf("Validating request params... FAILED [%s]", err.Error()) + message, status := middleware.GetErrorResponse(400, "Bad request. "+err.Error()) + http.Error(w, message, status) + return + } + log.Println("Validating request params... DONE") + + log.Println("Reading request body...") + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Printf("Reading request body... FAILED [%s]", err.Error()) + message, status := middleware.GetErrorResponse(500, "Server unable to read body. "+err.Error()) + http.Error(w, message, status) + } + r.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + + if len(body) == 0 { + message, status := middleware.GetErrorResponse(400, "Bad request. Body is empty.") + http.Error(w, message, status) + return + } + log.Println("Reading request body... DONE") + + log.Println("Marshalling request into XML object...") + var requestInXML CpixRequestType + err = xml.Unmarshal(body, &requestInXML) + if err != nil { + log.Printf("Marshalling request into XML object... FAILED [%s]", err.Error()) + message, status := middleware.GetErrorResponse(400, "Bad request. "+err.Error()) + http.Error(w, message, status) + return + } + log.Println("Marshalling request into XML object... DONE") + + // Resolve MPX domain for Key DS + resolvedUrl, err := resolveMpxDomainForService(mpxToken, mpxAccountId, "Key Data Service") + if err != nil { + message, status := middleware.GetErrorResponse(500, "Error resolving MPX Media Data Service url. "+err.Error()) + http.Error(w, message, status) + return + } + + // Check if key exists; if it does, get the id + mpxKeyIds, err := getMpxKeyIds(resolvedUrl, mpxToken, mpxAccountId, requestInXML.Id, "commonKey") + + if err != nil { + message, status := middleware.GetErrorResponse(500, "Error getting MPX key info. "+err.Error()) + http.Error(w, message, status) + return + } + + // Check if key exists; if it does, get the id else set it as "" + mpxKeyId := "" + if mpxKeyIds[0] == "" { + mpxKeyId = mpxKeyIds[0] + } + + err = postIntoMpxKeyDs(resolvedUrl, mpxToken, mpxAccountId, mpxKeyId, requestInXML.Id, requestInXML.ContentKeyList[0].Kid, _key) + + if err != nil { + message, status := middleware.GetErrorResponse(500, "Error POSTing new/updated key info. "+err.Error()) + http.Error(w, message, status) + return + } + + next.ServeHTTP(w, r) + }) +} + func sendSpekeResponse(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer next.ServeHTTP(w, r) @@ -150,6 +272,7 @@ func sendSpekeResponse(next http.Handler) http.Handler { http.Error(w, message, status) return } + log.Println("Reading request body... DONE") log.Println("Marshalling request into XML object...") var requestInXML CpixRequestType @@ -170,7 +293,7 @@ func sendSpekeResponse(next http.Handler) http.Handler { log.Println("Writing response headers... DONE") log.Println("Creating Static Speke XML body...") - response, err := buildStaticSpekeResponse(requestInXML.Id, requestInXML.ContentKeyList, requestInXML.DRMSystemList) + response, err := buildDynamicSpekeResponse(requestInXML.Id, requestInXML.ContentKeyList, requestInXML.DRMSystemList) if err != nil { log.Printf("Creating Static Speke XML body... FAILED \n [%s]", err.Error()) message, status := middleware.GetErrorResponse(400, "Bad request. "+err.Error()) @@ -190,6 +313,77 @@ func sendSpekeResponse(next http.Handler) http.Handler { }) } +func buildDynamicSpekeResponse(id string, contentKeys []ContentKeyType, drmSystems []DRMSystemType) ([]byte, error) { + + var resContentKeys = make([]ContentKeyType, len(contentKeys)) + + // Set the same static key & iv for each kid in the request + // Ideally we will want to create a different key and iv for every different kid + for i := 0; i < len(contentKeys); i++ { + resContentKeys[i].Kid = contentKeys[i].Kid + resContentKeys[i].Data = encode.BytesToBase64(encode.HexStringToBin(_key)) + resContentKeys[i].ExplicitIV = encode.BytesToBase64(encode.HexStringToBin(_iv)) + } + + // Now we set DRM specific data statically + // Ideally we'll want to pull this of a config + length := len(drmSystems) + resDrmSystems := make([]DRMSystemType, length) + sem := make(chan empty, length) // semaphore pattern + + // Here we use the semaphore pattern to parallelize the response for each drm system + for i, drmSystem := range drmSystems { + go func(i int, drmSystem DRMSystemType) { + log.Println(drmSystem.SystemId) + switch drmSystem.SystemId { + case _FAIRPLAY_SYSTEM_ID: + resDrmSystems[i].URIExtXKey = encode.BytesToBase64([]byte(_FAIRPLAY_URIEXTXKEY)) + resDrmSystems[i].KeyFormat = encode.BytesToBase64([]byte(_FAIRPLAY_KEYFORMAT)) + resDrmSystems[i].KeyFormatVersions = encode.BytesToBase64([]byte(_FAIRPLAY_KEYFORMATVERSIONS)) + break + case _WIDEVINE_SYSTEM_ID: + contentIdInBin := []byte(id) + contentKeyInBin := make([][]byte, len(contentKeys)) + + for i, contentKey := range contentKeys { + contentKeyInBin[i] = encode.HexStringToBin(strings.Replace(contentKey.Kid, "-", "", -1)) + } + + widevinePssh, err := generateWidevinePssh(contentKeyInBin, contentIdInBin, "widevine_test", "SD") + if err != nil { + return + } + + mp4Pssh, err := generateMp4Pssh(contentKeyInBin, strings.Replace(_WIDEVINE_SYSTEM_ID, "-", "", -1), widevinePssh) + if err != nil { + return + } + + resDrmSystems[i].Pssh = encode.BytesToBase64(mp4Pssh) + break + } + resDrmSystems[i].Kid = drmSystems[i].Kid + resDrmSystems[i].SystemId = drmSystems[i].SystemId + + sem <- empty{} + }(i, drmSystem) + } + // wait for goroutines to finish + for i := 0; i < length; i++ { + <-sem + } + + spekeResponse, err := xml.Marshal(CpixResponseType{Id: id, Cpix: _CPIX_URN, Pskc: _PSKC_URN, Speke: _SPEKE_URN, + ContentKeyList: resContentKeys, + DRMSystemList: resDrmSystems}) + + if err != nil { + return nil, err + } + + return spekeResponse, nil +} + func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystems []DRMSystemType) ([]byte, error) { var resContentKeys = make([]ContentKeyType, len(contentKeys)) @@ -223,7 +417,7 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem // TODO: implement proper (HTTP) error handling contentIdInBin := []byte(id) - contentKeyInBin := make([][]byte, len(contentKeys)) + contentKeyInBin := make([][]byte, len(contentKeys)) for i, contentKey := range contentKeys { contentKeyInBin[i] = encode.HexStringToBin(strings.Replace(contentKey.Kid, "-", "", -1)) @@ -336,3 +530,124 @@ func generateMp4Pssh(keyIdsInBin [][]byte, systemId string, drmPsshInBin []byte) return pssh, nil } + +func resolveMpxDomainForService(mpxToken string, escapedMpxAccountId string, service string) (string, error) { + log.Println("Resolving MPX media data service URL...") + url := "http://access.auth.theplatform.com/web/Registry/resolveUrlByAccount?schema=1.1&token=" + mpxToken + "&form=json" + "&_accountId=" + escapedMpxAccountId + "&_service=" + url.PathEscape(service) + "&httpError=true" + mpxRequest, err := http.NewRequest(http.MethodGet, url, nil) + + client := &http.Client{} + mpxResponse, err := client.Do(mpxRequest) + + if err == nil && mpxResponse.StatusCode != 200 { + err = errors.New(mpxResponse.Status) + log.Printf("Resolving MPX media data service URL... FAILED [http status: [%s]", err.Error()) + return "", err + } else if err != nil { + dump, _ := httputil.DumpResponse(mpxResponse, true) + log.Printf("%s", dump) + + // handle error + log.Printf("Resolving MPX media data service URL... FAILED [%s]", err.Error()) + return "", err + } + + log.Println("Resolving MPX media data service URL... DONE") + mpxResponseData, err := ioutil.ReadAll(mpxResponse.Body) + mpxResponse.Body.Close() + + log.Println("Marshalling MPX response into JSON object...") + var resolvedMpxUrlInJson ResolveMpxUrlType + err = json.Unmarshal(mpxResponseData, &resolvedMpxUrlInJson) + if err != nil { + log.Printf("Marshalling request into JSON object... FAILED [%s]", err.Error()) + return "", errors.New("Error unmarshalling MPX response. " + err.Error()) + } + + log.Println("Marshalling request into JSON object... DONE") + + return resolvedMpxUrlInJson.ResolvedUrl, nil +} + +func postIntoMpxKeyDs(resolvedUrl string, mpxToken string, mpxAccountId string, mpxKeyId string, contentId string, keyId string, key string) error { + //Put the key into KeyDS + log.Println("POST(TING) key into Key DS... ") + log.Println(" Formatting Key DS request body... ") + mpxRequestBody, err := json.Marshal(MpxKeyDsType{mpxKeyId, contentId, contentId, mpxAccountId, "commonKey", url.PathEscape(mpxAccountId), key, len(key), "SD", "literal"}) + if err != nil { + //TODO: ERROR HANDLING + } + log.Println(" Formatting Key DS request body... DONE") + + // Put the Key into KeyDS + url := resolvedUrl + "/data/Key?httpError=true&schema=1.2.1&token=" + mpxToken + "&form=cjson" + mpxRequest, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(mpxRequestBody)) + + client := http.Client{} + mpxResponse, err := client.Do(mpxRequest) + + if err == nil && mpxResponse.StatusCode >= 300 { + err = errors.New(mpxResponse.Status) + } else if err != nil { + dump, _ := httputil.DumpResponse(mpxResponse, true) + log.Printf("%s", dump) + + // handle error + log.Printf("POST(TING) key into Key DS... FAILED [%s]", err.Error()) + return err + } + + log.Println("POST(TING) key into Key DS... DONE") + + return nil +} + +func getMpxKeyIds(resolvedUrl string, mpxToken string, mpxAccountId string, context string, algorithm string) ([]string, error) { + //Put the key into KeyDS + log.Println("GET(ting) key info from Key DS... ") + log.Println(" Formatting Key DS request params... ") + urlParams := "?httpError=true&schema=1.2.1&form=cjson&byAlgorithm=" + algorithm + "&byContext=" + context + "&token=" + mpxToken + "&account=" + url.PathEscape(mpxAccountId) + log.Println(" Formatting Key DS request params... DONE") + + // Put the Key into KeyDS + url := resolvedUrl + "/data/Key" + urlParams + mpxRequest, err := http.NewRequest(http.MethodGet, url, nil) + + client := http.Client{} + mpxResponse, err := client.Do(mpxRequest) + + if err == nil && mpxResponse.StatusCode >= 300 { + dump, _ := httputil.DumpResponse(mpxResponse, true) + log.Printf("%s", dump) + + err = errors.New(mpxResponse.Status) + // handle error + log.Printf("GET(ting) key from Key DS... FAILED [%s]", err.Error()) + return nil, err + + } else if err != nil { + return nil, err + } + + log.Println("GET(ting) key from Key DS... DONE") + + mpxResponseData, err := ioutil.ReadAll(mpxResponse.Body) + mpxResponse.Body.Close() + + log.Println("Marshalling MPX response into JSON object...") + var mpxKeyDsEntries MpxKeyDsArrayType + err = json.Unmarshal(mpxResponseData, &mpxKeyDsEntries) + if err != nil { + log.Printf("Marshalling request into JSON object... FAILED [%s]", err.Error()) + return nil, errors.New("Error unmarshalling MPX response. " + err.Error()) + } + + log.Println("Marshalling request into JSON object... DONE") + + keyIds := make([]string, len(mpxKeyDsEntries.Entries)) + for i, entry := range mpxKeyDsEntries.Entries { + keyIds[i] = entry.Id + } + + return keyIds, nil +} From bd2772120a1f4fb68544dc7fb355d1d06f630c1c Mon Sep 17 00:00:00 2001 From: Saravanan Date: Fri, 20 Apr 2018 01:43:51 +0800 Subject: [PATCH 13/16] fixed bugs --- deltaGenericKeyProvider.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 5ff3894..f8c3dba 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -238,7 +238,7 @@ func setKeysOnMpxKeyDs(next http.Handler) http.Handler { // Check if key exists; if it does, get the id else set it as "" mpxKeyId := "" - if mpxKeyIds[0] == "" { + if len(mpxKeyIds) != 0 && mpxKeyIds[0] == "" { mpxKeyId = mpxKeyIds[0] } @@ -573,7 +573,7 @@ func postIntoMpxKeyDs(resolvedUrl string, mpxToken string, mpxAccountId string, //Put the key into KeyDS log.Println("POST(TING) key into Key DS... ") log.Println(" Formatting Key DS request body... ") - mpxRequestBody, err := json.Marshal(MpxKeyDsType{mpxKeyId, contentId, contentId, mpxAccountId, "commonKey", url.PathEscape(mpxAccountId), key, len(key), "SD", "literal"}) + mpxRequestBody, err := json.Marshal(MpxKeyDsType{mpxKeyId, contentId, contentId, mpxAccountId, "commonKey", keyId, key, len(key) / 2, "SD", "literal"}) if err != nil { //TODO: ERROR HANDLING } From a64f5c152e721e1967aae7142498a627fe460dd4 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Fri, 20 Apr 2018 02:06:47 +0800 Subject: [PATCH 14/16] fixed bug where POST to MPX did not include id --- deltaGenericKeyProvider.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index f8c3dba..652c0c9 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -238,7 +238,7 @@ func setKeysOnMpxKeyDs(next http.Handler) http.Handler { // Check if key exists; if it does, get the id else set it as "" mpxKeyId := "" - if len(mpxKeyIds) != 0 && mpxKeyIds[0] == "" { + if len(mpxKeyIds) != 0 && mpxKeyIds[0] != "" { mpxKeyId = mpxKeyIds[0] } @@ -292,15 +292,15 @@ func sendSpekeResponse(next http.Handler) http.Handler { w.Header().Set("Speke-User-Agent", _SPEKE_UA) log.Println("Writing response headers... DONE") - log.Println("Creating Static Speke XML body...") + log.Println("Creating Dynamic Speke XML body...") response, err := buildDynamicSpekeResponse(requestInXML.Id, requestInXML.ContentKeyList, requestInXML.DRMSystemList) if err != nil { - log.Printf("Creating Static Speke XML body... FAILED \n [%s]", err.Error()) + log.Printf("Creating Dynamic Speke XML body... FAILED \n [%s]", err.Error()) message, status := middleware.GetErrorResponse(400, "Bad request. "+err.Error()) http.Error(w, message, status) return } - log.Println("Creating Static Speke XML body... DONE") + log.Println("Creating Dynamic Speke XML body... DONE") log.Println("Writing response body...") if _, err := w.Write(response); err != nil { @@ -573,6 +573,7 @@ func postIntoMpxKeyDs(resolvedUrl string, mpxToken string, mpxAccountId string, //Put the key into KeyDS log.Println("POST(TING) key into Key DS... ") log.Println(" Formatting Key DS request body... ") + println(keyId) mpxRequestBody, err := json.Marshal(MpxKeyDsType{mpxKeyId, contentId, contentId, mpxAccountId, "commonKey", keyId, key, len(key) / 2, "SD", "literal"}) if err != nil { //TODO: ERROR HANDLING @@ -583,6 +584,9 @@ func postIntoMpxKeyDs(resolvedUrl string, mpxToken string, mpxAccountId string, url := resolvedUrl + "/data/Key?httpError=true&schema=1.2.1&token=" + mpxToken + "&form=cjson" mpxRequest, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(mpxRequestBody)) + dump, _ := httputil.DumpRequest(mpxRequest, true) + log.Printf("%s", dump) + client := http.Client{} mpxResponse, err := client.Do(mpxRequest) From 50457919a68ef8fcd92c08391c52d497fd25d3e6 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Fri, 20 Apr 2018 11:27:06 +0800 Subject: [PATCH 15/16] fixed key id bug --- deltaGenericKeyProvider.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 652c0c9..67a4f76 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -346,7 +346,7 @@ func buildDynamicSpekeResponse(id string, contentKeys []ContentKeyType, drmSyste contentKeyInBin := make([][]byte, len(contentKeys)) for i, contentKey := range contentKeys { - contentKeyInBin[i] = encode.HexStringToBin(strings.Replace(contentKey.Kid, "-", "", -1)) + contentKeyInBin[i] = encode.HexStringToBin(removeAllHypens(contentKey.Kid)) } widevinePssh, err := generateWidevinePssh(contentKeyInBin, contentIdInBin, "widevine_test", "SD") @@ -354,7 +354,7 @@ func buildDynamicSpekeResponse(id string, contentKeys []ContentKeyType, drmSyste return } - mp4Pssh, err := generateMp4Pssh(contentKeyInBin, strings.Replace(_WIDEVINE_SYSTEM_ID, "-", "", -1), widevinePssh) + mp4Pssh, err := generateMp4Pssh(contentKeyInBin, removeAllHypens(_WIDEVINE_SYSTEM_ID), widevinePssh) if err != nil { return } @@ -428,7 +428,7 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem return } - mp4Pssh, err := generateMp4Pssh(contentKeyInBin, strings.Replace(_WIDEVINE_SYSTEM_ID, "-", "", -1), widevinePssh) + mp4Pssh, err := generateMp4Pssh(contentKeyInBin, removeAllHypens(_WIDEVINE_SYSTEM_ID), widevinePssh) if err != nil { return } @@ -574,7 +574,7 @@ func postIntoMpxKeyDs(resolvedUrl string, mpxToken string, mpxAccountId string, log.Println("POST(TING) key into Key DS... ") log.Println(" Formatting Key DS request body... ") println(keyId) - mpxRequestBody, err := json.Marshal(MpxKeyDsType{mpxKeyId, contentId, contentId, mpxAccountId, "commonKey", keyId, key, len(key) / 2, "SD", "literal"}) + mpxRequestBody, err := json.Marshal(MpxKeyDsType{mpxKeyId, contentId, contentId, mpxAccountId, "commonKey", removeAllHypens(keyId), key, len(key) / 2, "SD", "literal"}) if err != nil { //TODO: ERROR HANDLING } @@ -655,3 +655,7 @@ func getMpxKeyIds(resolvedUrl string, mpxToken string, mpxAccountId string, cont return keyIds, nil } + +func removeAllHypens(original string) string { + return strings.Replace(original, "-", "", -1) +} From f13c1de8fb1032573b5ec147f16ac5424c332d2e Mon Sep 17 00:00:00 2001 From: Saravanan Date: Fri, 20 Apr 2018 11:39:25 +0800 Subject: [PATCH 16/16] fixed wrong key y response --- deltaGenericKeyProvider.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deltaGenericKeyProvider.go b/deltaGenericKeyProvider.go index 67a4f76..17341f2 100644 --- a/deltaGenericKeyProvider.go +++ b/deltaGenericKeyProvider.go @@ -393,8 +393,8 @@ func buildStaticSpekeResponse(id string, contentKeys []ContentKeyType, drmSystem log.Printf("length of content keys %d", len(contentKeys)) for i := 0; i < len(contentKeys); i++ { resContentKeys[i].Kid = contentKeys[i].Kid - resContentKeys[i].Data = encode.BytesToBase64(encode.HexStringToBin(_KEY)) - resContentKeys[i].ExplicitIV = encode.BytesToBase64(encode.HexStringToBin(_IV)) + resContentKeys[i].Data = encode.BytesToBase64(encode.HexStringToBin(_key)) + resContentKeys[i].ExplicitIV = encode.BytesToBase64(encode.HexStringToBin(_iv)) } // Now we set DRM specific data statically