/// <summary> /// initial netlogon signature token when AES is negotiated /// </summary> /// <param name="sequenceNumber">sequence number</param> /// <param name="sessionKey">session key</param> /// <param name="requestConfidentiality">confidentiality is required or not</param> /// <param name="isClientSideOutbound">Is client side outbound message.</param> /// <param name="securityBuffers"> /// Security buffers, contains input plain-text; output cipher-text and signature. /// </param> private static void InitialNetlogonSignatureTokenWhenAesIsNegotiated( ref ulong sequenceNumber, byte[] sessionKey, bool requestConfidentiality, bool isClientSideOutbound, SecurityBuffer[] securityBuffers) { byte[] plainText; byte[] cipherText; byte[] token; NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign = new NL_AUTH_SHA2_SIGNATURE(); //The SignatureAlgorithm first byte MUST be set to 0x13, //and the second byte MUST be set to 0x00. nlAuthSha2Sign.SignatureAlgorithm = NL_AUTH_SHA2_SIGNATURE_SignatureAlgorithm_Values.HMACSHA256; if (requestConfidentiality) { //If the Confidentiality option (section 3.3.1) is //requested from the application, then the SealAlgorithm //first byte MUST be set to 0x1A, the second byte MUST //be set to 0x00, and the Confounder MUST be filled with //cryptographically random data. nlAuthSha2Sign.SealAlgorithm = NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.AES128; nlAuthSha2Sign.Confounder = GenerateNonce(NL_AUTH_SIGNATURE_CONFOUNDER_LENGTH); } else { //If the Confidentiality option (section 3.3.1) is not //requested, then the SealAlgorithm MUST be filled with //two bytes of 0xff, and the Confounder is not included in the token. nlAuthSha2Sign.SealAlgorithm = NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.NotEncrypted; nlAuthSha2Sign.Confounder = null; } //The Pad MUST be filled with 0xff bytes. nlAuthSha2Sign.Pad = NL_AUTH_SHA2_SIGNATURE_Pad_Values.V1; //The Flags MUST be filled with 0x00 bytes. nlAuthSha2Sign.Flags = Flags_Values.V1; //The SequenceNumber MUST be computed using the following algorithm. nlAuthSha2Sign.SequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound); //The ClientSequenceNumber MUST be incremented by 1. sequenceNumber += 1; //Compute signature plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers); nlAuthSha2Sign.Checksum = ComputeSignatureWhenAesIsNegotiated( nlAuthSha2Sign, sessionKey, requestConfidentiality, plainText); //If the Confidentiality option is requested, the data and the Confounder //field MUST be encrypted. If AES is negotiated then the server MUST use //the AES-128 algorithm using the SessionKey with an initialization //vector constructed by concatenating the sequence number with //itself twice (thus getting 16 bytes of data) if (requestConfidentiality) { byte[] aesEncryptionKey = ComputeEncryptionKeyWhenAesIsNegotiated(sessionKey); using (BCryptAlgorithm aes = new BCryptAlgorithm("AES")) { aes.Mode = BCryptCipherMode.CFB; aes.Key = aesEncryptionKey; aes.IV = ArrayUtility.ConcatenateArrays( nlAuthSha2Sign.SequenceNumber, nlAuthSha2Sign.SequenceNumber); nlAuthSha2Sign.Confounder = aes.Encrypt(nlAuthSha2Sign.Confounder); plainText = SspiUtility.ConcatenateReadWriteSecurityBuffers( securityBuffers, SecurityBufferType.Data); cipherText = aes.Encrypt(plainText); } SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, cipherText); } else { cipherText = null; } //The SequenceNumber MUST be encrypted. //If AES is negotiated, then the server MUST use the AES-128 //algorithm using the SessionKey with an initialization vector //constructed by concatenating the first 8 bytes of the //checksum with itself twice (thus getting 16 bytes of data) using (BCryptAlgorithm aes = new BCryptAlgorithm("AES")) { aes.Mode = BCryptCipherMode.CFB; aes.Key = sessionKey; aes.IV = ArrayUtility.ConcatenateArrays( nlAuthSha2Sign.Checksum, // Checksum is only 8 bytes nlAuthSha2Sign.Checksum); nlAuthSha2Sign.SequenceNumber = aes.Encrypt(nlAuthSha2Sign.SequenceNumber); } nlAuthSha2Sign.Dummy = GenerateNonce(NL_AUTH_SHA2_SIGNATURE_DUMMY_LENGTH); // 24 == size of dummy token = TypeMarshal.ToBytes(nlAuthSha2Sign); SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token, token); }
/// <summary> /// Compute signature when AES is negotiated /// </summary> /// <param name="nlAuthSha2Sign">NL_AUTH_SHA2_SIGNATURE struct</param> /// <param name="sessionKey">session key</param> /// <param name="requestConfidentiality">confidentiality is required or not</param> /// <param name="plainText">plain-text message to make signature</param> /// <returns>signature</returns> private static byte[] ComputeSignatureWhenAesIsNegotiated( NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign, byte[] sessionKey, bool requestConfidentiality, byte[] plainText) { //If AES is negotiated, then a signature MUST be computed using //the following algorithm: //CALL SHA256Reset(&HashContext, Sk, sizeof(Sk)); //CALL SHA256Input(HashContext, NL_AUTH_SHA2_SIGNATURE, [8 bytes]); //IF Confidentiality requested //CALL SHA256Input(HashContext, Confounder, [8 bytes]); //CALL SHA256FinalBits(HashContext, Message, size of Message; //CALL SHA256Result(HashContext, output); //SET Signature to output //Note: In the first call to SHA256Input, only the first 8-bytes //of the NL_AUTH_SHA2_SIGNATURE structure are used. byte[] signature; byte[] buf = ArrayUtility.ConcatenateArrays( BitConverter.GetBytes((ushort)nlAuthSha2Sign.SignatureAlgorithm), BitConverter.GetBytes((ushort)nlAuthSha2Sign.SealAlgorithm), BitConverter.GetBytes((ushort)nlAuthSha2Sign.Pad), BitConverter.GetBytes((ushort)nlAuthSha2Sign.Flags)); if (requestConfidentiality) { buf = ArrayUtility.ConcatenateArrays( buf, nlAuthSha2Sign.Confounder); } buf = ArrayUtility.ConcatenateArrays( buf, plainText); using (HMACSHA256 hmacSha256 = new HMACSHA256(sessionKey)) { signature = hmacSha256.ComputeHash(buf); } //After the signature is computed, the signature MUST be truncated, //with only the first 8 bytes being copied into the Checksum field //of NL_AUTH_SHA2_SIGNATURE. signature = ArrayUtility.SubArray(signature, 0, NL_AUTH_SIGNATURE_CHECKSUM_LENGTH); return signature; }