static BOOL negotiate_write_neg_token(PSecBuffer output_buffer, NegToken* token)
{
	WINPR_ASSERT(output_buffer);
	WINPR_ASSERT(token);

	BOOL ret = FALSE;
	WinPrAsn1Encoder* enc = NULL;
	WinPrAsn1_MemoryChunk mechTypes = { token->mechTypes.cbBuffer, token->mechTypes.pvBuffer };
	WinPrAsn1_OctetString mechToken = { token->mechToken.cbBuffer, token->mechToken.pvBuffer };
	WinPrAsn1_OctetString mechListMic = { token->mic.cbBuffer, token->mic.pvBuffer };
	wStream s;
	size_t len;

	enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
	if (!enc)
		return FALSE;

	/* For NegTokenInit wrap in an initialContextToken */
	if (token->init)
	{
		/* InitialContextToken [APPLICATION 0] IMPLICIT SEQUENCE */
		if (!WinPrAsn1EncAppContainer(enc, 0))
			goto cleanup;

		/* thisMech MechType OID */
		if (!WinPrAsn1EncOID(enc, &spnego_OID))
			goto cleanup;
	}

	/* innerContextToken [0] NegTokenInit or [1] NegTokenResp */
	if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1))
		goto cleanup;

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

	/* mechTypes [0] MechTypeList (mechTypes already contains the SEQUENCE tag) */
	if (token->init)
	{
		if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes))
			goto cleanup;
		WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
	}
	/* negState [0] ENUMERATED */
	else if (token->negState != NOSTATE)
	{
		if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState))
			goto cleanup;
		WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);
	}

	/* supportedMech [1] OID */
	if (token->supportedMech.len)
	{
		if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech))
			goto cleanup;
		WLog_DBG(TAG, "\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));
	}

	/* mechToken [2] OCTET STRING */
	if (token->mechToken.cbBuffer)
	{
		if (!WinPrAsn1EncContextualOctetString(enc, 2, &mechToken))
			goto cleanup;
		WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);
	}

	/* mechListMIC [3] OCTET STRING */
	if (token->mic.cbBuffer)
	{
		if (!WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic))
			goto cleanup;
		WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);
	}

	/* NegTokenInit or NegTokenResp */
	if (!WinPrAsn1EncEndContainer(enc))
		goto cleanup;

	if (token->init)
	{
		/* initialContextToken */
		if (!WinPrAsn1EncEndContainer(enc))
			goto cleanup;
	}

	if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer)
		goto cleanup;

	Stream_StaticInit(&s, output_buffer->pvBuffer, len);

	if (WinPrAsn1EncToStream(enc, &s))
	{
		output_buffer->cbBuffer = len;
		ret = TRUE;
	}

cleanup:
	WinPrAsn1Encoder_Free(&enc);
	return ret;
}
static BOOL negotiate_write_neg_token(PSecBuffer output_buffer, NegToken* token)
{
	WINPR_ASSERT(output_buffer);
	WINPR_ASSERT(token);

	BOOL ret = FALSE;
	WinPrAsn1Encoder* enc = NULL;
	WinPrAsn1_MemoryChunk mechTypes = { token->mechTypes.cbBuffer, token->mechTypes.pvBuffer };
	WinPrAsn1_OctetString mechToken = { token->mechToken.cbBuffer, token->mechToken.pvBuffer };
	WinPrAsn1_OctetString mechListMic = { token->mic.cbBuffer, token->mic.pvBuffer };
	wStream s;
	size_t len;

	enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
	if (!enc)
		return FALSE;

	 
	if (token->init)
	{
		 
		if (!WinPrAsn1EncAppContainer(enc, 0))
			goto cleanup;

		 
		if (!WinPrAsn1EncOID(enc, &spnego_OID))
			goto cleanup;
	}

	 
	if (!WinPrAsn1EncContextualSeqContainer(enc, token->init ? 0 : 1))
		goto cleanup;

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

	 
	if (token->init)
	{
		if (!WinPrAsn1EncContextualRawContent(enc, 0, &mechTypes))
			goto cleanup;
		WLog_DBG(TAG, "\tmechTypes [0] (%li bytes)", token->mechTypes.cbBuffer);
	}
	 
	else if (token->negState != NOSTATE)
	{
		if (!WinPrAsn1EncContextualEnumerated(enc, 0, token->negState))
			goto cleanup;
		WLog_DBG(TAG, "\tnegState [0] (%d)", token->negState);
	}

	 
	if (token->supportedMech.len)
	{
		if (!WinPrAsn1EncContextualOID(enc, 1, &token->supportedMech))
			goto cleanup;
		WLog_DBG(TAG, "\tsupportedMech [1] (%s)", negotiate_mech_name(&token->supportedMech));
	}

	 
	if (token->mechToken.cbBuffer)
	{
		if (!WinPrAsn1EncContextualOctetString(enc, 2, &mechToken))
			goto cleanup;
		WLog_DBG(TAG, "\tmechToken [2] (%li bytes)", token->mechToken.cbBuffer);
	}

	 
	if (token->mic.cbBuffer)
	{
		if (!WinPrAsn1EncContextualOctetString(enc, 3, &mechListMic))
			goto cleanup;
		WLog_DBG(TAG, "\tmechListMIC [3] (%li bytes)", token->mic.cbBuffer);
	}

	 
	if (!WinPrAsn1EncEndContainer(enc))
		goto cleanup;

	if (token->init)
	{
		 
		if (!WinPrAsn1EncEndContainer(enc))
			goto cleanup;
	}

	if (!WinPrAsn1EncStreamSize(enc, &len) || len > output_buffer->cbBuffer)
		goto cleanup;

	Stream_StaticInit(&s, output_buffer->pvBuffer, len);

	if (WinPrAsn1EncToStream(enc, &s))
	{
		output_buffer->cbBuffer = len;
		ret = TRUE;
	}

cleanup:
	WinPrAsn1Encoder_Free(&enc);
	return ret;
}