// Der encoding/decoding
const uint8_t* der_decode_RecoveryKeyBag(CFAllocatorRef allocator,
                                         SOSRecoveryKeyBagRef* RecoveryKeyBag, CFErrorRef *error,
                                         const uint8_t* der, const uint8_t *der_end) {
    if (der == NULL) return der;
    const uint8_t *result = NULL;
    
    SOSRecoveryKeyBagRef rb = CFTypeAllocate(SOSRecoveryKeyBag, struct __OpaqueSOSRecoveryKeyBag, allocator);
    require_quiet(SecAllocationError(rb, error, CFSTR("Recovery bag allocation failed")), fail);
    
    const uint8_t *sequence_end = NULL;
    der = ccder_decode_sequence_tl(&sequence_end, der, der_end);
    require_quiet(sequence_end == der_end, fail);
    
    der = der_decode_string(kCFAllocatorDefault, kCFPropertyListImmutable, &rb->accountDSID, error, der, sequence_end);
    rb->generation = SOSGenCountCreateFromDER(kCFAllocatorDefault, error, &der, sequence_end);
    der = ccder_decode_uint64(&rb->rkbVersion, der, sequence_end);
    der = der_decode_data(allocator, kCFPropertyListImmutable, &rb->recoveryKeyBag, error, der, sequence_end);
    
    require_quiet(SecRequirementError(der == der_end, error, CFSTR("Extra space in sequence")), fail);
    if (RecoveryKeyBag) CFTransferRetained(*RecoveryKeyBag, rb);
    result = der;
fail:
    CFReleaseNull(rb);
    return result;
}
static const uint8_t *der_decode_pbkdf2_params(size_t *saltLen, const uint8_t **salt,
                                               unsigned long *iterationCount,
                                               unsigned long *keyLength,
                                               const uint8_t *der, const uint8_t *der_end)
{
    const uint8_t * body_end = NULL;
    der = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &body_end, der, der_end);

    if (body_end != der_end)
        der = NULL;

    size_t salt_size = 0;
    const uint8_t *salt_bytes = NULL;

    der = ccder_decode_tl(CCDER_OCTET_STRING, &salt_size, der, body_end);
    salt_bytes = der;
    der += salt_size;

    uint64_t iteration_count = 0;
    uint64_t key_len = 0;
    der = ccder_decode_uint64(&iteration_count, der, body_end);
    if (iteration_count > UINT32_MAX)
        der = NULL;

    der = ccder_decode_uint64(&key_len, der, body_end);
    if (key_len > UINT32_MAX)
        der = NULL;

    der = der_expect_SecAsn1Oid(&CSSMOID_PKCS5_HMAC_SHA1, der, body_end);

    if (der) {
        if (salt)
            *salt = salt_bytes;
        if (saltLen)
            *saltLen = salt_size;
        if (iterationCount)
            *iterationCount = (unsigned long)iteration_count;
        if (keyLength)
            *keyLength = (unsigned long) key_len;
    }

    return der;
}