View Single Post
Old 8th January 2019, 06:51   #267  |  Link
maetel99
Registered User
 
Join Date: Apr 2018
Posts: 21
Here is some code I wrote to use a device key to determine a processing key via the subset-difference process. This code essentially consolidates the libaacs functions _find_dk(), _calc_mk_dks(), and _calc_v_mask(), along with some macros. The input to the function is a candidate device key and an index into the record arrays for the encrypted media key and subset-differences. The function returns a processing key if the device key is valid, otherwise it returns an error code. I'm not including all the lower-level functions here.

There is some ambiguity in the AACS spec as far as what record array index to start from when testing a candidate device key in the subset-difference tree. My code follows the model used in libaacs in which we simply test every index for a given device key and look for the first one that delivers a valid processing key. Unfortunately, this can be slow since there may be 500+ records to test and each one requires iterating over a subset-difference tree.

Code:
// attempts to derive a processing key from a given device key and subset difference record index
// follows logic from AACS Common Spec section 3.2.4
// sdIdx = index of subset difference record to use
// deviceKey = 16 byte device key to use
// processingKey = 16 byte processing key if sucessful
// mediaKey = 16 byte media key if successful
// returns 0 if found processing key, -1 if no valid key, -2 if device key is revoked
int DeriveProcessingKeyForDeviceKeySubsetRecord(BDAACSMKBStatus *mkbStat, int sdIdx,
    unsigned char *deviceKey, unsigned char *processingKey, unsigned char *mediaKey)
{
    int                 result;
    int32_t             tmvIter;
    uint32_t            m, n, mu, mv, uv, mvIter;
    unsigned char       uvNumber[4], devKey[16], verifyData[16], encDataKey[16], procKey[16], medKey[16];
    
    // get the verification data (MKB record type 0x81)
    BDAACSMKBGetVerificationData(mkbStat, verifyData);
    // get encrypted data key for this subset difference index (MKB record type 0x05)
    BDAACSMKBGetMediaKeyData(mkbStat, sdIdx, encDataKey);
    // get subset diffence data for this index (MKB record type 0x04)
    BDAACSMKBGetSubsetDiffData(mkbStat, sdIdx, &mu, uvNumber);
    
    // check u mask value from record
    // AACS common spec section 3.2.3:
    // if the u mask value is not of the bit form 00xxxxxx, this marks the end of the list
    if ((mu & 0xC0) != 0) {
        // end of the list, device at this record location is revoked
        return -2;
    }
    
    // compute uv from uvNumber in endian independent way
    uv = uvNumber[0];
    uv = (uv << 8) | uvNumber[1];
    uv = (uv << 8) | uvNumber[2];
    uv = (uv << 8) | uvNumber[3];
    if (uv == 0)
        return -1;
    
    // following AACS common spec section 3.2.3, derive v mask mv from the uv number
    // the v mask is given by the first lower-order 1-bit in the uv number
    // that bit, and all lower-order 0-bits, are zero bits in the v mask
    mv = 0xFFFFFFFF;
    while ((uv & ~mv) == 0)
        mv <<= 1;
    // following AACS common spec section 3.2.5.1.5, encoded u-mask value is actually
    // the number of low-order 0-bits in the mask
    mu = 0xFFFFFFFF << mu;
    
    // section 3.2.3: the mu mask always has more trailing 0 bits than the mv mask
    // the deeper the position of a node in the tree, the shorter the sequence of 0-bits
    // in the mask associated to that node, and u is an ancestor of v
    if (mv < mu)
        return -1;
    
    // initialize iterator and starting device key
    mvIter = mv;
    result = -1;
    
    while (mvIter != mu) {
        
        // start with initial device key
        memcpy(devKey, deviceKey, 16);
        
        // repeat subset-difference iteration as needed
        tmvIter = mvIter;
        while (tmvIter != mv) {
            // determine the bit position of the most significant 0 bit in iterator
            // use algorithm from Hacker's Delight, number of trailing zeros
            m = ~tmvIter & (tmvIter - 1);
            n = 1;
            while (m != 0) {
                n <<= 1;
                m >>= 1;
            }
            n >>= 1;
            // determine whether to take a right or a left subsidiary Device Key
            if ((uv & n) == 0) {
                // use left subsidiary Device key
                AACSEncryptAESG3(devKey, devKey, 0, 0);
            }
            else {
                // use right subsidiary Device key
                AACSEncryptAESG3(devKey, 0, 0, devKey);
            }
            
            // arithmetic shift right using signed integer
            tmvIter >>= 1;
        }
        
        // get processing key at this position
        AACSEncryptAESG3(devKey, 0, procKey, 0);
        // attempt to derive media key using this processing key to check if it is valid
        result = AACSDeriveMediaKeyFromProcessKey(procKey, encDataKey, uvNumber, verifyData, medKey);
        if (result == 0) {
            // valid processing key
            if (processingKey)
                memcpy(processingKey, procKey, 16);
            if (mediaKey)
                memcpy(mediaKey, medKey, 16);
            mvIter = mu;
        }
        else
            mvIter <<= 1;
    }
    
    return result;
}
maetel99 is offline   Reply With Quote