3.1.4.1 GetKey (Opnum 0)

The syntax for the GetKey (Opnum 0) method consists of the following.

 HRESULT GetKey(
         [in] handle_t hBinding,
         [in] ULONG cbTargetSD,
         [in] [size_is(cbTargetSD)] [ref] char* pbTargetSD,
         [in] [unique] GUID* pRootKeyID,
         [in] LONG L0KeyID,
         [in] LONG L1KeyID,
         [in] LONG L2KeyID,
         [out] unsigned long* pcbOut,
         [out] [size_is(, *pcbOut)] byte** ppbOut);
  

hBinding: This is an RPC binding handle parameter, as specified in [C706] and [MS-RPCE] section 2.

cbTargetSD: This parameter is equal to the length, in bytes, of the security descriptor supplied in pbTargetSD.

pbTargetSD: This parameter is a pointer to the security descriptor for which the group key is being requested.

pRootKeyID: This parameter represents the root key identifier of the requested key. It can be set to NULL.

L0KeyID: This parameter represents the L0 index of the requested group key. It MUST be a signed 32-bit integer greater than or equal to -1.

L1KeyID: This parameter represents the L1 index of the requested group key. It MUST be a signed 32-bit integer between -1 and 31 (inclusive).

L2KeyID: This parameter represents the L2 index of the requested group key. It MUST be a 32-bit integer between -1 and 31 (inclusive).

pcbOut: This parameter is an unsigned, 32-bit integer. It MUST be equal to the length, in bytes, of the data returned in ppbOut.

ppbOut: On successful processing of a request, the server MUST set this to a pointer that refers to the output key binary large object (BLOB), as specified in section 2.2.4.

Return Values: The server MUST return zero if it successfully processes the message received from the client; otherwise, it MUST return a nonzero value.

Exceptions Thrown: No exceptions are thrown beyond those thrown by the underlying RPC protocol [MS-RPCE].

Processing rules for the GetKey request are specified herein. In general, there are four types of requests, as follows:

  • When the pRootKeyID parameter is equal to NULL and the values of the L0KeyID, L1KeyID, and L2KeyID parameters are all equal to -1, the latest group key is being requested by the caller.

  • When the pRootKeyID parameter is equal to NULL and the values of the L0KeyID, L1KeyID, and L2KeyID parameters are all greater than -1, the seed key used at a specific time in the past is being requested by the caller.

  • When the pRootKeyID parameter is not equal to NULL and the values of the L0KeyID, L1KeyID, and L2KeyID parameters are all equal to -1, the latest group key derived from the specified root key is being requested by the caller.

  • When the pRootKeyID parameter is not equal to NULL and the values of the L0KeyID, L1KeyID, and L2KeyID parameters are all greater than -1, a specific seed key is being requested by the caller.

When a Group Key Distribution Protocol server receives the GetKey request, it must first validate that the pbTargetSD parameter is a valid security descriptor in self-relative format. The server MUST also verify that the L0KeyID, L1KeyID, and L2KeyID parameters are either all equal to -1 or all greater than or equal to 0. If any of these conditions are not met, the server MUST return an error and exit.

The server MUST further validate its arguments as follows:

  1. Retrieve the current time in the FILETIME format specified in [MS-DTYP] section 2.3.3. Construct an unsigned 64-bit number by setting the low-order word to dwLowDateTime and the high-order word to dwHighDateTime. Call this 64-bit number CurrentTime.

  2. Convert the CurrentTime value to a group key identifier (L0, L1, L2) as follows; the division operator in the following calculations represents integer division:

    • L0 = CurrentTime / (32 * 32 * 3.6 * 1011)

    • L1 = (CurrentTime mod (32 * 32 * 3.6 * 1011)) / (32 * 3.6 * 1011)

    • L2 = (CurrentTime mod (32 * 3.6 * 1011)) / (3.6 * 1011)

  3. If (L0KeyID, L1KeyID, L2KeyID) is lexically greater than (L0, L1, L2), then return an error and exit.

  4. Otherwise, compute a group key identifier (GKID) as follows:

    • If the pRootKeyID parameter is equal to NULL and L0KeyID >= 0, GKID = (L0KeyID, L1KeyID, L2KeyID).

    • Otherwise, if the pRootKeyID parameter is not equal to NULL and 0 =< L0KeyID < L0, GKID = (L0KeyID, 31, 31).

    • Otherwise, GKID = (L0, L1, L2).

  5. Using the method specified in [MS-DTYP] section 2.5.3.2, perform an access check with the pbTargetSD parameter value as the SecurityDescriptor, the caller's authorization context as the Token, 0x3 as the Access Request mask, and with the Object Tree and PrincipalSelfSubst SID set to NULL. If access is granted, the client is authorized to access seed keys. Otherwise, if access is not granted, proceed to step 6.

  6. If the L0KeyID, L1KeyID, and L2KeyID parameters are not all equal to -1, return an error and exit.

  7. Otherwise, using the method specified in [MS-DTYP] section 2.5.3.2, perform an access check with pbTargetSD parameter value as the SecurityDescriptor, the caller's authorization context as the Token, 0x2 as the Access Request mask, and with the Object Tree and PrincipalSelfSubst SID set to NULL. If access is granted, the client is only authorized to access public keys. Otherwise, if access is not granted, return an error and exit.

The server MUST then determine whether it is running on a writable DC or a read-only domain controller (RODC) via implementation-specific means. If it is running on an RODC, the server MUST process the request as a client, as specified in section 3.2.4.1. Specifically, it MUST look for a cached key using the group key identifier GKID computed in the previous step 4. If a matching key is not found in the cache, the server MUST forward the request to a writable DC with group key identifier (L0KeyID, L1KeyID, L2KeyID), as specified by the caller.

If the access check in the previous step 5 was successful, the server MUST return the seed key obtained from the cache or the writable DC directly to the client, without performing any of the actions specified in section 3.2.4.3. Otherwise, if a seed key is found (section 3.2.4.1), the server MUST convert it to a public key with the requested group key identifier, using the method specified in section 3.1.4.1.2, and return the result to the caller.

If the server is running on a writable DC, it MUST proceed as follows:

  1. If the pRootKeyID parameter is not NULL, locate the root key object whose CN matches the pRootKeyID parameter value and proceed to step 5. If no such root key object is found, return an error and exit.

  2. If the L0KeyID, L1KeyID, and L2KeyID parameters are all equal to -1, select the root key object that has the highest value in its msKds-UseStartTime attribute. If the set of root keys is empty, create a new root key by using the method specified in section 3.1.4.1.1 and proceed to step 5.

  3. Convert the GKID value to an unsigned 64-bit number by reversing the method specified in step 2 of the previous procedure. Let this number be denoted KeyStartTime.

  4. From the set of root key objects, select the subset of root keys that have the msKds-UseStartTime attribute value less than or equal to KeyStartTime. If this subset contains more than one root key object, select the one with the highest msKds-CreateTime attribute value. If no suitable root key object can be found, return an error and exit.

  5. Let the root key object selected in the above steps be denoted RK. Compute the seed key corresponding to security descriptor pbTargetSD, root key RK, and group key identifier GKID, as specified in section 3.1.4.1.2. Let this seed key be denoted SK.

  6. If the client is only authorized to access public keys, as determined by the access checks in steps 5 and 7 of the previous procedure, compute the public key corresponding to the SK, as specified in section 3.1.4.1.2. Return the result in the ppbOut parameter of the GetKey method, by using the format specified in section 2.2.4, and then exit.

  7. If the client is authorized to access seed keys, as determined by the access check in step 5 of the previous procedure, then:

    • If the L2 component of GKID is equal to 31, return the L1 seed key corresponding to SK in the ppbOut parameter by using the format specified in section 2.2.4, with the L2 key field omitted.

    • Otherwise, if the L1 component of GKID is equal to 0, return SK in the ppbOut parameter by using the format specified in section 2.2.4, with the L1 key field omitted.

    • If neither of the above two cases apply, construct the return value in the ppbOut parameter by using the format specified in section 2.2.4, with SK in the L2 key field and the next older L1 seed key in the L1 key field.