Skip to content

Commit 17d2305

Browse files
committed
Added NFold algorithm from krb5.
1 parent a680453 commit 17d2305

4 files changed

Lines changed: 159 additions & 39 deletions

File tree

NtApiDotNet/NtApiDotNet.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<Compile Include="Utilities\Memory\SafeBufferWrapper.cs" />
104104
<Compile Include="Utilities\SafeBuffers\SafeGuidArrayBuffer.cs" />
105105
<Compile Include="Utilities\Security\MD4.cs" />
106+
<Compile Include="Utilities\Security\NFold.cs" />
106107
<Compile Include="Utilities\Security\ObjectTypeTree.cs" />
107108
<Compile Include="Utilities\Security\ARC4.cs" />
108109
<Compile Include="Utilities\Text\HexDumpBuilder.cs" />
@@ -360,6 +361,7 @@
360361
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosErrorType.cs" />
361362
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosMessageType.cs" />
362363
<Compile Include="Win32\Security\Authentication\Kerberos\KerberosNameType.cs" />
364+
<Compile Include="Win32\Security\Authentication\Kerberos\Ndr\PacDeviceInfoParser.cs" />
363365
<Compile Include="Win32\Security\Authentication\Kerberos\PrincipalName.cs" />
364366
<Compile Include="Win32\Security\Authentication\Negotiate\NegotiateAuthenticationToken.cs" />
365367
<Compile Include="Win32\Security\Authentication\Ntlm\NtlmAuthenticateAuthenticationTokenV2.cs" />
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright 2020 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Original license from KRB5 source code on which this code is derived.
16+
/*
17+
* Copyright (C) 1998 by the FundsXpress, INC.
18+
*
19+
* All rights reserved.
20+
*
21+
* Export of this software from the United States of America may require
22+
* a specific license from the United States Government. It is the
23+
* responsibility of any person or organization contemplating export to
24+
* obtain such a license before exporting.
25+
*
26+
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
27+
* distribute this software and its documentation for any purpose and
28+
* without fee is hereby granted, provided that the above copyright
29+
* notice appear in all copies and that both that copyright notice and
30+
* this permission notice appear in supporting documentation, and that
31+
* the name of FundsXpress. not be used in advertising or publicity pertaining
32+
* to distribution of the software without specific, written prior
33+
* permission. FundsXpress makes no representations about the suitability of
34+
* this software for any purpose. It is provided "as is" without express
35+
* or implied warranty.
36+
*
37+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
38+
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
39+
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
40+
*/
41+
42+
using System.Text;
43+
44+
namespace NtApiDotNet.Utilities.Security
45+
{
46+
/// <summary>
47+
/// Class to perform the n-fold operation for Kerberos key derivation.
48+
/// </summary>
49+
public static class NFold
50+
{
51+
/// <summary>
52+
/// Perform an n-fold operation.
53+
/// </summary>
54+
/// <param name="in_data">The input data as a string.</param>
55+
/// <param name="out_length">The output length in bytes.</param>
56+
/// <returns>The computed n-folded byte array.</returns>
57+
public static byte[] Compute(string in_data, int out_length)
58+
{
59+
return Compute(Encoding.ASCII.GetBytes(in_data), out_length);
60+
}
61+
62+
/// <summary>
63+
/// Perform an n-fold operation.
64+
/// </summary>
65+
/// <param name="in_data">The input data.</param>
66+
/// <param name="out_length">The output length in bytes.</param>
67+
/// <returns>The computed n-folded byte array.</returns>
68+
public static byte[] Compute(byte[] in_data, int out_length)
69+
{
70+
int a, b, c, lcm;
71+
int byte_val, i, msbit;
72+
73+
int in_length = in_data.Length;
74+
75+
/* first compute lcm(n,k) */
76+
77+
a = out_length;
78+
b = in_length;
79+
80+
while (b != 0)
81+
{
82+
c = b;
83+
b = a % b;
84+
a = c;
85+
}
86+
87+
lcm = out_length * in_length / a;
88+
89+
/* now do the real work */
90+
91+
byte[] out_data = new byte[out_length];
92+
93+
byte_val = 0;
94+
95+
/* this will end up cycling through k lcm(k,n)/k times, which
96+
is correct */
97+
for (i = lcm - 1; i >= 0; i--)
98+
{
99+
/* compute the msbit in k which gets added into this byte */
100+
msbit = (/* first, start with the msbit in the first, unrotated
101+
byte */
102+
((in_length << 3) - 1)
103+
/* then, for each byte, shift to the right for each
104+
repetition */
105+
+ (((in_length << 3) + 13) * (i / in_length))
106+
/* last, pick out the correct byte within that
107+
shifted repetition */
108+
+ ((in_length - (i % in_length)) << 3)
109+
) % (in_length << 3);
110+
111+
/* pull out the byte value itself */
112+
byte_val += (((in_data[((in_length - 1) - (msbit >> 3)) % in_length] << 8) |
113+
(in_data[((in_length) - (msbit >> 3)) % in_length]))
114+
>> ((msbit & 7) + 1)) & 0xff;
115+
116+
/* do the addition */
117+
byte_val += out_data[i % out_length];
118+
out_data[i % out_length] = (byte)(byte_val & 0xff);
119+
120+
/* keep around the carry bit, if any */
121+
byte_val >>= 8;
122+
123+
}
124+
125+
/* if there's a carry bit left over, add it back in */
126+
if (byte_val != 0)
127+
{
128+
for (i = out_length - 1; i >= 0; i--)
129+
{
130+
/* do the addition */
131+
byte_val += out_data[i];
132+
out_data[i] = (byte)(byte_val & 0xff);
133+
134+
/* keep around the carry bit, if any */
135+
byte_val >>= 8;
136+
}
137+
}
138+
139+
return out_data;
140+
}
141+
}
142+
}

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosEncryptedData.cs

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
using NtApiDotNet.Utilities.Security;
1717
using NtApiDotNet.Utilities.Text;
1818
using System;
19-
using System.ComponentModel;
2019
using System.IO;
20+
using System.Linq;
2121
using System.Security.Cryptography;
2222
using System.Text;
2323

@@ -93,16 +93,6 @@ private bool DecryptRC4WithKey(KerberosKey key, KeyUsage key_usage, out byte[] d
9393
private const int AES_CHECKSUM_SIZE = 12;
9494
private const int AES_CONFOUNDER_SIZE = 16;
9595

96-
private static byte[] _aes_encrypt_ticket = new byte[] { 0xB5, 0xB0, 0x58, 0x2C, 0x14, 0xB6, 0x50, 0x0A, 0xAD, 0x56, 0xAB, 0x55, 0xAA, 0x80, 0x55, 0x6A };
97-
private static byte[] _aes_verify_ticket = new byte[] { 0x62, 0xDC, 0x6E, 0x37, 0x1A, 0x63, 0xA8, 0x09, 0x58, 0xAC, 0x56, 0x2B, 0x15, 0x40, 0x4A, 0xC5 };
98-
private static byte[] _aes_encrypt_auth = new byte[] { 0xFE, 0x54, 0xAA, 0x55, 0xA5, 0x02, 0x52, 0x2F, 0xBF, 0x5F, 0xAF, 0xD7, 0xEA, 0x81, 0x75, 0xFA };
99-
private static byte[] _aes_verify_auth = new byte[] { 0xAB, 0x80, 0xC0, 0x60, 0xAA, 0xAF, 0xAA, 0x2E, 0x6A, 0xB5, 0x5A, 0xAD, 0x55, 0x41, 0x6B, 0x55 };
100-
101-
private static byte[] _aes_encrypt_ap_rep = new byte[] { 0x05, 0xD7, 0xEC, 0x76, 0xB5, 0x0B, 0x53, 0x33, 0xC1, 0x60, 0xB0, 0x58, 0x2A, 0x81, 0x96, 0x0B };
102-
private static byte[] _aes_verify_ap_rep = new byte[] { 0xB3, 0x04, 0x02, 0x81, 0xBA, 0xB8, 0xAB, 0x32, 0x6C, 0xB6, 0x5B, 0x2D, 0x95, 0x41, 0x8B, 0x65 };
103-
private static byte[] _aes_encrypt_krb_cred = new byte[] { 0x15, 0xE0, 0x70, 0xB8, 0xD5, 0x1C, 0x53, 0x3B, 0xC5, 0x62, 0xB1, 0x58, 0xAA, 0x81, 0xD6, 0x2B };
104-
private static byte[] _aes_verify_krb_cred = new byte[] { 0xC3, 0x0C, 0x86, 0xC3, 0xDA, 0xC9, 0xAB, 0x3A, 0x70, 0xB8, 0x5C, 0x2E, 0x15, 0x41, 0xCB, 0x85 };
105-
10696
private static void SwapEndBlocks(byte[] cipher_text)
10797
{
10898
if (cipher_text.Length < AES_BLOCK_SIZE*2)
@@ -134,32 +124,21 @@ private byte[] DecryptAESBlock(byte[] key, byte[] cipher_text, int offset)
134124
return block;
135125
}
136126

137-
private bool DecryptAESWithKey(KerberosKey key, KeyUsage key_usage, out byte[] decrypted)
127+
private const byte EncryptionKey = 0xAA;
128+
private const byte VerificationKey = 0x55;
129+
130+
private byte[] DeriveTempKey(KerberosKey key, KeyUsage key_usage, byte key_type)
138131
{
139-
byte[] derive_enc_key;
140-
byte[] derive_mac_key;
132+
byte[] r = BitConverter.GetBytes((int)key_usage).Reverse().ToArray();
133+
Array.Resize(ref r, 5);
134+
r[4] = key_type;
135+
return NFold.Compute(r, 16);
136+
}
141137

142-
switch (key_usage)
143-
{
144-
case KeyUsage.AsRepTgsRepTicket:
145-
derive_enc_key = _aes_encrypt_ticket;
146-
derive_mac_key = _aes_verify_ticket;
147-
break;
148-
case KeyUsage.ApReqAuthSubKey:
149-
derive_enc_key = _aes_encrypt_auth;
150-
derive_mac_key = _aes_verify_auth;
151-
break;
152-
case KeyUsage.ApRepEncryptedPart:
153-
derive_enc_key = _aes_encrypt_ap_rep;
154-
derive_mac_key = _aes_verify_ap_rep;
155-
break;
156-
case KeyUsage.KrbCred:
157-
derive_enc_key = _aes_encrypt_krb_cred;
158-
derive_mac_key = _aes_verify_krb_cred;
159-
break;
160-
default:
161-
throw new ArgumentException("Unknown key usage type.");
162-
}
138+
private bool DecryptAESWithKey(KerberosKey key, KeyUsage key_usage, out byte[] decrypted)
139+
{
140+
byte[] derive_enc_key = DeriveTempKey(key, key_usage, EncryptionKey);
141+
byte[] derive_mac_key = DeriveTempKey(key, key_usage, VerificationKey);
163142

164143
byte[] new_key = KerberosKey.DeriveAesKey(key.Key, derive_enc_key);
165144

NtApiDotNet/Win32/Security/Authentication/Kerberos/KerberosKey.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,8 @@ private static byte[] GetKey(string key)
247247

248248
private static byte[] DeriveAesKey(string password, string salt, int iterations, int key_size)
249249
{
250-
// "kerberos" n-folded out to 16 bytes.
251-
byte[] folded_key = { 0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73, 0x7b, 0x9b, 0x5b, 0x2b, 0x93, 0x13, 0x2b, 0x93 };
252-
253250
Rfc2898DeriveBytes pbkdf = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), iterations);
254-
return DeriveAesKey(pbkdf.GetBytes(key_size), folded_key);
251+
return DeriveAesKey(pbkdf.GetBytes(key_size), NFold.Compute("kerberos", 16));
255252
}
256253

257254
internal static byte[] DeriveAesKey(byte[] base_key, byte[] folded_key)

0 commit comments

Comments
 (0)