static BOOL negotiate_read_neg_token(PSecBuffer input, NegToken* token)
{
	WinPrAsn1Decoder dec;
	WinPrAsn1Decoder dec2;
	WinPrAsn1_OID oid;
	WinPrAsn1_tagId contextual;
	WinPrAsn1_tag tag;
	size_t len;
	WinPrAsn1_OctetString octet_string;
	BOOL err;

	WINPR_ASSERT(input);
	WINPR_ASSERT(token);

	WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);

	if (!WinPrAsn1DecPeekTag(&dec, &tag))
		return FALSE;

	if (tag == 0x60)
	{
		/* initialContextToken [APPLICATION 0] */
		if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
			return FALSE;
		dec = dec2;

		/* thisMech OID */
		if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
			return FALSE;

		if (!sspi_gss_oid_compare(&spnego_OID, &oid))
			return FALSE;

		/* [0] NegTokenInit */
		if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))
			return FALSE;

		token->init = TRUE;
	}
	/* [1] NegTokenResp */
	else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))
		return FALSE;
	dec = dec2;

	WLog_DBG(TAG, token->init ? "Reading negTokenInit..." : "Reading negTokenResp...");

	/* Read NegTokenResp sequence members */
	do
	{
		if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))
			return FALSE;

		switch (contextual)
		{
			case 0:
				if (token->init)
				{
					/* mechTypes [0] MechTypeList */
					wStream s = WinPrAsn1DecGetStream(&dec2);
					token->mechTypes.BufferType = SECBUFFER_TOKEN;
					token->mechTypes.cbBuffer = Stream_Length(&s);
					token->mechTypes.pvBuffer = Stream_Buffer(&s);
					WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
				}
				else
				{
					/* negState [0] ENUMERATED */
					if (!WinPrAsn1DecReadEnumerated(&dec2, &token->negState))
						return FALSE;
					WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);
				}
				break;
			case 1:
				if (token->init)
				{
					/* reqFlags [1] ContextFlags BIT STRING (ignored) */
					if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))
						return FALSE;
					WLog_DBG(TAG, "\treqFlags [1] (%li bytes)", len);
				}
				else
				{
					/* supportedMech [1] MechType */
					if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))
						return FALSE;
					WLog_DBG(TAG, "\tsupportedMech [1] (%s)",
					         negotiate_mech_name(&token->supportedMech));
				}
				break;
			case 2:
				/* mechToken [2] OCTET STRING */
				if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
					return FALSE;
				token->mechToken.cbBuffer = octet_string.len;
				token->mechToken.pvBuffer = octet_string.data;
				token->mechToken.BufferType = SECBUFFER_TOKEN;
				WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", octet_string.len);
				break;
			case 3:
				/* mechListMic [3] OCTET STRING */
				if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
					return FALSE;
				token->mic.cbBuffer = octet_string.len;
				token->mic.pvBuffer = octet_string.data;
				token->mic.BufferType = SECBUFFER_TOKEN;
				WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", octet_string.len);
				break;
			default:
				WLog_ERR(TAG, "unknown contextual item %d", contextual);
				return FALSE;
		}
	} while (WinPrAsn1DecPeekTag(&dec, &tag));

	return TRUE;
}
static BOOL negotiate_read_neg_token(PSecBuffer input, NegToken* token)
{
	WinPrAsn1Decoder dec;
	WinPrAsn1Decoder dec2;
	WinPrAsn1_OID oid;
	WinPrAsn1_tagId contextual;
	WinPrAsn1_tag tag;
	size_t len;
	WinPrAsn1_OctetString octet_string;
	BOOL err;

	WINPR_ASSERT(input);
	WINPR_ASSERT(token);

	WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, input->pvBuffer, input->cbBuffer);

	if (!WinPrAsn1DecPeekTag(&dec, &tag))
		return FALSE;

	if (tag == 0x60)
	{
		 
		if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
			return FALSE;
		dec = dec2;

		 
		if (!WinPrAsn1DecReadOID(&dec, &oid, FALSE))
			return FALSE;

		if (!sspi_gss_oid_compare(&spnego_OID, &oid))
			return FALSE;

		 
		if (!WinPrAsn1DecReadContextualSequence(&dec, 0, &err, &dec2))
			return FALSE;

		token->init = TRUE;
	}
	 
	else if (!WinPrAsn1DecReadContextualSequence(&dec, 1, &err, &dec2))
		return FALSE;
	dec = dec2;

	WLog_DBG(TAG, token->init ? "Reading negTokenInit..." : "Reading negTokenResp...");

	 
	do
	{
		if (!WinPrAsn1DecReadContextualTag(&dec, &contextual, &dec2))
			return FALSE;

		switch (contextual)
		{
			case 0:
				if (token->init)
				{
					 
					wStream s = WinPrAsn1DecGetStream(&dec2);
					token->mechTypes.BufferType = SECBUFFER_TOKEN;
					token->mechTypes.cbBuffer = Stream_Length(&s);
					token->mechTypes.pvBuffer = Stream_Buffer(&s);
					WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
				}
				else
				{
					 
					if (!WinPrAsn1DecReadEnumerated(&dec2, &token->negState))
						return FALSE;
					WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);
				}
				break;
			case 1:
				if (token->init)
				{
					 
					if (!WinPrAsn1DecPeekTagAndLen(&dec2, &tag, &len) || (tag != ER_TAG_BIT_STRING))
						return FALSE;
					WLog_DBG(TAG, "\treqFlags [1] (%li bytes)", len);
				}
				else
				{
					 
					if (!WinPrAsn1DecReadOID(&dec2, &token->supportedMech, FALSE))
						return FALSE;
					WLog_DBG(TAG, "\tsupportedMech [1] (%s)",
					         negotiate_mech_name(&token->supportedMech));
				}
				break;
			case 2:
				 
				if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
					return FALSE;
				token->mechToken.cbBuffer = octet_string.len;
				token->mechToken.pvBuffer = octet_string.data;
				token->mechToken.BufferType = SECBUFFER_TOKEN;
				WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", octet_string.len);
				break;
			case 3:
				 
				if (!WinPrAsn1DecReadOctetString(&dec2, &octet_string, FALSE))
					return FALSE;
				token->mic.cbBuffer = octet_string.len;
				token->mic.pvBuffer = octet_string.data;
				token->mic.BufferType = SECBUFFER_TOKEN;
				WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", octet_string.len);
				break;
			default:
				WLog_ERR(TAG, "unknown contextual item %d", contextual);
				return FALSE;
		}
	} while (WinPrAsn1DecPeekTag(&dec, &tag));

	return TRUE;
}