From fe8fc831e51a585a465da500e411327d0ce71544 Mon Sep 17 00:00:00 2001 From: Steve Syfuhs Date: Thu, 26 Mar 2026 11:01:17 -0700 Subject: [PATCH 1/4] FAST and ASN --- .gitignore | 1 + Kerberos.NET/Client/FastArmorContext.cs | 245 +++ Kerberos.NET/Client/KerberosClient.cs | 71 +- Kerberos.NET/Configuration/Krb5KdcDefaults.cs | 8 + .../Entities/Krb/KrbApRep.generated.cs | 189 -- .../Entities/Krb/KrbApReq.generated.cs | 213 --- .../Entities/Krb/KrbAsRep.generated.cs | 43 - .../Entities/Krb/KrbAsReq.generated.cs | 43 - .../Krb/KrbAuthenticator.generated.cs | 311 ---- .../Krb/KrbAuthorizationData.generated.cs | 158 -- .../Entities/Krb/KrbChecksum.generated.cs | 158 -- .../Entities/Krb/KrbCred.generated.cs | 217 --- .../Entities/Krb/KrbCredInfo.generated.cs | 344 ---- .../Krb/KrbETypeInfo2Entry.generated.cs | 186 -- .../Entities/Krb/KrbEncApRepPart.generated.cs | 209 --- .../Entities/Krb/KrbEncAsRepPart.generated.cs | 43 - .../Krb/KrbEncKdcRepPart.generated.cs | 363 ---- .../Krb/KrbEncKrbCredPart.generated.cs | 279 --- .../Krb/KrbEncKrbPrivPart.generated.cs | 256 --- .../Krb/KrbEncTgsRepPart.generated.cs | 43 - .../Krb/KrbEncTicketPart.generated.cs | 344 ---- .../Krb/KrbEncryptedData.generated.cs | 184 -- .../Krb/KrbEncryptionKey.generated.cs | 158 -- .../Entities/Krb/KrbError.generated.cs | 365 ---- .../Entities/Krb/KrbHostAddress.generated.cs | 161 -- .../Entities/Krb/KrbKdcRep.generated.cs | 250 --- .../Entities/Krb/KrbKdcReq.generated.cs | 211 --- .../Entities/Krb/KrbKdcReqBody.generated.cs | 395 ---- .../Entities/Krb/KrbLastReq.generated.cs | 150 -- .../Entities/Krb/KrbPaData.generated.cs | 158 -- .../Entities/Krb/KrbPaEncTsEnc.generated.cs | 159 -- .../Krb/KrbPaSvrReferralData.generated.cs | 46 +- .../Krb/KrbPrincipalName.generated.cs | 173 -- .../Entities/Krb/KrbPriv.generated.cs | 184 -- .../Entities/Krb/KrbTgsRep.generated.cs | 43 - .../Entities/Krb/KrbTgsReq.generated.cs | 43 - .../Entities/Krb/KrbTicket.generated.cs | 188 -- .../Krb/KrbTransitedEncoding.generated.cs | 158 -- .../Entities/Pkinit/KrbAuthPack.generated.cs | 242 --- .../Pkinit/KrbDHReplyInfo.generated.cs | 174 -- ...rbExternalPrincipalIdentifier.generated.cs | 209 --- .../Pkinit/KrbKdcDHKeyInfo.generated.cs | 184 -- .../Pkinit/KrbPKAuthenticator.generated.cs | 201 -- .../Entities/Pkinit/KrbPaPkAsRep.generated.cs | 178 -- .../Entities/Pkinit/KrbPaPkAsReq.generated.cs | 225 --- .../Entities/SpNego/NegTokenInit.generated.cs | 236 --- .../Entities/SpNego/NegTokenResp.generated.cs | 217 --- .../SpNego/NegotiationToken.generated.cs | 154 -- Kerberos.NET/Kerberos.NET.csproj | 65 +- Kerberos.NET/Server/FastState.cs | 37 + Kerberos.NET/Server/KdcAsReqMessageHandler.cs | 77 +- Kerberos.NET/Server/KdcServer.cs | 6 + .../Server/PaDataEncryptedChallengeHandler.cs | 137 ++ Kerberos.NET/Server/PaDataFastHandler.cs | 313 ++++ Kerberos.NET/kerberos.asn | 19 +- Kerberos.NET/kerberos.asn.json | 456 +++++ Tests/Tests.Kerberos.NET/Kdc/FastTests.cs | 331 ++++ Tools/Asn1CodeGen/Asn1Ast.cs | 152 ++ Tools/Asn1CodeGen/Asn1CodeGen.csproj | 16 + Tools/Asn1CodeGen/Asn1Lexer.cs | 292 +++ Tools/Asn1CodeGen/Asn1Parser.cs | 799 ++++++++ Tools/Asn1CodeGen/Asn1Token.cs | 97 + Tools/Asn1CodeGen/CSharpCodeEmitter.cs | 1638 +++++++++++++++++ Tools/Asn1CodeGen/Program.cs | 130 ++ Tools/Asn1CodeGen/testdata/KerberosV5.asn | 28 + .../Asn1IncrementalGenerator.cs | 257 +++ .../Asn1SourceGenerator.csproj | 30 + 67 files changed, 5166 insertions(+), 8484 deletions(-) create mode 100644 Kerberos.NET/Client/FastArmorContext.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbApRep.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbApReq.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbAsRep.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbAsReq.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbAuthenticator.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbAuthorizationData.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbChecksum.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbCred.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbCredInfo.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncApRepPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncAsRepPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncTicketPart.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncryptedData.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncryptionKey.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbError.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbHostAddress.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbKdcRep.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbKdcReq.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbKdcReqBody.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbLastReq.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaData.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPrincipalName.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPriv.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbTgsRep.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbTgsReq.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbTicket.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbTransitedEncoding.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbAuthPack.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.generated.cs delete mode 100644 Kerberos.NET/Entities/SpNego/NegTokenInit.generated.cs delete mode 100644 Kerberos.NET/Entities/SpNego/NegTokenResp.generated.cs delete mode 100644 Kerberos.NET/Entities/SpNego/NegotiationToken.generated.cs create mode 100644 Kerberos.NET/Server/FastState.cs create mode 100644 Kerberos.NET/Server/PaDataEncryptedChallengeHandler.cs create mode 100644 Kerberos.NET/Server/PaDataFastHandler.cs create mode 100644 Kerberos.NET/kerberos.asn.json create mode 100644 Tests/Tests.Kerberos.NET/Kdc/FastTests.cs create mode 100644 Tools/Asn1CodeGen/Asn1Ast.cs create mode 100644 Tools/Asn1CodeGen/Asn1CodeGen.csproj create mode 100644 Tools/Asn1CodeGen/Asn1Lexer.cs create mode 100644 Tools/Asn1CodeGen/Asn1Parser.cs create mode 100644 Tools/Asn1CodeGen/Asn1Token.cs create mode 100644 Tools/Asn1CodeGen/CSharpCodeEmitter.cs create mode 100644 Tools/Asn1CodeGen/Program.cs create mode 100644 Tools/Asn1CodeGen/testdata/KerberosV5.asn create mode 100644 Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs create mode 100644 Tools/Asn1SourceGenerator/Asn1SourceGenerator.csproj diff --git a/.gitignore b/.gitignore index 036c443f..f82e3842 100644 --- a/.gitignore +++ b/.gitignore @@ -288,3 +288,4 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs +Tools/Asn1CodeGen/testdata/*.generated.cs diff --git a/Kerberos.NET/Client/FastArmorContext.cs b/Kerberos.NET/Client/FastArmorContext.cs new file mode 100644 index 00000000..0515330f --- /dev/null +++ b/Kerberos.NET/Client/FastArmorContext.cs @@ -0,0 +1,245 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Text; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using static Kerberos.NET.Entities.KerberosConstants; + +namespace Kerberos.NET.Client +{ + /// + /// Manages FAST (RFC 6113) armor state for client-side AS-REQ armoring. + /// Wraps outgoing requests in FAST armor and unwraps incoming FAST responses. + /// + public class FastArmorContext + { + /// + /// The armor ticket (TGT) used to establish the FAST tunnel. + /// + public KrbKdcRep ArmorTgt { get; } + + /// + /// The decrypted enc-part of the armor TGT providing the session key. + /// + public KrbEncKdcRepPart ArmorTgtEncPart { get; } + + /// + /// The derived FAST armor key protecting the tunnel. + /// + public KerberosKey ArmorKey { get; private set; } + + /// + /// The authenticator subkey used in the armor AP-REQ. + /// + private KrbEncryptionKey subkey; + + public FastArmorContext(KrbKdcRep armorTgt, KrbEncKdcRepPart armorTgtEncPart) + { + this.ArmorTgt = armorTgt ?? throw new ArgumentNullException(nameof(armorTgt)); + this.ArmorTgtEncPart = armorTgtEncPart ?? throw new ArgumentNullException(nameof(armorTgtEncPart)); + } + + /// + /// Wraps a KDC request in FAST armor, returning the modified request + /// with PA_FX_FAST PA-Data added. + /// + public KrbAsReq WrapRequest(KrbAsReq asReq) + { + if (asReq == null) + { + throw new ArgumentNullException(nameof(asReq)); + } + + var sessionKey = this.ArmorTgtEncPart.Key; + var etype = sessionKey.EType; + + // Generate a subkey for the armor AP-REQ + this.subkey = KrbEncryptionKey.Generate(etype); + + // Create the armor AP-REQ using the armor TGT + Now(out DateTimeOffset ctime, out int cusec); + + var authenticator = new KrbAuthenticator + { + CName = this.ArmorTgt.CName, + CRealm = this.ArmorTgt.CRealm, + CTime = ctime, + CuSec = cusec, + SequenceNumber = GetNonce(), + Subkey = this.subkey + }; + + var armorApReq = new KrbApReq + { + Ticket = this.ArmorTgt.Ticket, + Authenticator = KrbEncryptedData.Encrypt( + authenticator.EncodeApplication(), + sessionKey.AsKey(), + KeyUsage.ApReqAuthenticator + ) + }; + + // Derive the armor key: CF2(subkey, ticket_session_key, "subkeyarmor", "ticketarmor") + var armorKeyBytes = KrbFx.Cf2( + this.subkey.KeyValue.ToArray(), + sessionKey.KeyValue.ToArray(), + Encoding.UTF8.GetBytes("subkeyarmor"), + Encoding.UTF8.GetBytes("ticketarmor"), + etype + ); + + this.ArmorKey = new KerberosKey(key: armorKeyBytes.ToArray(), etype: etype); + + // Build the inner FAST request with the original PA-Data and req-body + var fastReq = new KrbFastReq + { + FastOptions = FastOptions.Reserved, + PaData = asReq.PaData ?? Array.Empty(), + ReqBody = asReq.Body + }; + + // Compute the checksum over the outer KDC-REQ-BODY + var outerBodyEncoded = asReq.Body.Encode(); + var requestChecksum = KrbChecksum.Create(outerBodyEncoded, this.ArmorKey, KeyUsage.FastReqChecksum); + + // Encrypt the inner FAST request + var encFastReq = KrbEncryptedData.Encrypt( + fastReq.Encode(), + this.ArmorKey, + KeyUsage.FastEnc + ); + + // Build the armored request + var armoredReq = new KrbFastArmoredReq + { + Armor = new KrbFastArmor + { + Type = KrbArmorType.FX_FAST_ARMOR_AP_REQUEST, + Value = armorApReq.EncodeApplication() + }, + RequestChecksum = requestChecksum, + EncryptedFastRequest = encFastReq + }; + + var fastRequest = new KrbPaFxFastRequest + { + ArmoredData = armoredReq + }; + + // Add PA_FX_FAST to the outer request's PA-Data + var outerPaData = new[] + { + new KrbPaData + { + Type = PaDataType.PA_FX_FAST, + Value = fastRequest.Encode() + } + }; + + return new KrbAsReq + { + Body = asReq.Body, + PaData = outerPaData, + MessageType = asReq.MessageType, + ProtocolVersionNumber = asReq.ProtocolVersionNumber + }; + } + + /// + /// Unwraps a FAST response from an AS-REP, extracting the inner PA-Data + /// and applying the strengthen key to the reply key. + /// + /// The AS-REP containing FAST PA-Data + /// The reply key to strengthen; will be replaced with the strengthened key + /// The FAST response containing inner PA-Data and finished message + public KrbFastResponse UnwrapResponse(KrbAsRep asRep, ref KerberosKey replyKey) + { + if (asRep == null) + { + throw new ArgumentNullException(nameof(asRep)); + } + + if (this.ArmorKey == null) + { + throw new InvalidOperationException("Armor key has not been established. Call WrapRequest first."); + } + + var paFast = asRep.PaData?.FirstOrDefault(p => p.Type == PaDataType.PA_FX_FAST); + + if (paFast == null) + { + return null; + } + + var fastReply = KrbPaFxFastReply.Decode(paFast.Value); + var armoredRep = fastReply.ArmoredData; + + // Decrypt the FAST response using the armor key + var fastResponse = armoredRep.EncFastRep.Decrypt( + this.ArmorKey, + KeyUsage.FastRep, + b => KrbFastResponse.Decode(b) + ); + + // Strengthen the reply key if a strengthen key is present + if (fastResponse.StrengthenKey != null && replyKey != null) + { + var etype = this.ArmorKey.EncryptionType; + + var strengthenedKeyBytes = KrbFx.Cf2( + replyKey.GetKey().ToArray(), + fastResponse.StrengthenKey.KeyValue.ToArray(), + Encoding.UTF8.GetBytes("strengthenkey"), + Encoding.UTF8.GetBytes("replykey"), + etype + ); + + replyKey = new KerberosKey(key: strengthenedKeyBytes.ToArray(), etype: etype); + } + + return fastResponse; + } + + /// + /// Extracts FAST error response from a KRB-ERROR when FAST is active. + /// + public KrbFastResponse UnwrapError(KrbError error) + { + if (error?.EData == null || this.ArmorKey == null) + { + return null; + } + + try + { + var methodData = KrbMethodData.Decode(error.EData.Value); + + var paFast = methodData.MethodData?.FirstOrDefault(p => p.Type == PaDataType.PA_FX_FAST); + + if (paFast == null) + { + return null; + } + + var fastReply = KrbPaFxFastReply.Decode(paFast.Value); + var armoredRep = fastReply.ArmoredData; + + return armoredRep.EncFastRep.Decrypt( + this.ArmorKey, + KeyUsage.FastRep, + b => KrbFastResponse.Decode(b) + ); + } + catch + { + // If we can't parse the FAST error, fall through to normal error handling + return null; + } + } + } +} diff --git a/Kerberos.NET/Client/KerberosClient.cs b/Kerberos.NET/Client/KerberosClient.cs index ee1519d2..b75d48d5 100644 --- a/Kerberos.NET/Client/KerberosClient.cs +++ b/Kerberos.NET/Client/KerberosClient.cs @@ -208,6 +208,21 @@ public TimeSpan ReceiveTimeout /// public bool CacheServiceTickets { get; set; } = true; + /// + /// Indicates whether the client should use FAST (RFC 6113) armor + /// when performing AS-REQ authentication. When enabled and an armor + /// ticket is available, the client will wrap requests in a FAST tunnel. + /// + public bool UseFast { get; set; } + + /// + /// The FAST armor context used for the current authentication session. + /// Set automatically when FAST is enabled and an armor TGT is available, + /// or set explicitly to provide a specific armor ticket. + /// + [KerberosIgnore] + public FastArmorContext FastArmor { get; set; } + /// /// Indicates that the cache should override configuration and always store in memory only. /// Defaults to true for backwards compatibility. @@ -639,13 +654,26 @@ private void ConfigurePreAuth(KerberosCredential credential, KerberosProtocolExc // the usual case is KDC requires pre-auth and it's provided hints to what it's // willing to accept for this. Usually it's ETypes and Salt information for pwd - credential.IncludePreAuthenticationHints(pex?.Error?.DecodePreAuthentication()); + var preAuthHints = pex?.Error?.DecodePreAuthentication(); + + credential.IncludePreAuthenticationHints(preAuthHints); foreach (var salt in credential.Salts) { this.logger.LogDebug("AS-REP PA-Data: EType = {Etype}; Salt = {Salt};", salt.Key, salt.Value); } + // Detect if KDC advertises FAST support (PA_FX_FAST in pre-auth hints) + if (this.UseFast && preAuthHints != null) + { + var fastHint = preAuthHints.FirstOrDefault(p => p.Type == PaDataType.PA_FX_FAST); + + if (fastHint != null) + { + this.logger.LogDebug("KDC advertises FAST support"); + } + } + // now we try pre-auth this.AuthenticationOptions |= AuthenticationOptions.PreAuthenticate; @@ -1348,13 +1376,35 @@ private async Task RequestTgt(KerberosCredential credential, KrbPrincipalName tg string.Join("/", asReqMessage.Body.SName.Name) ); + // Wrap the request in FAST armor if enabled and an armor context is available + if (this.UseFast && this.FastArmor != null) + { + asReqMessage = this.FastArmor.WrapRequest(asReqMessage); + } + var asRep = await this.transport.SendMessage(credential.Domain, asReqMessage).ConfigureAwait(false); - var decrypted = credential.DecryptKdcRep( - asRep, - KeyUsage.EncAsRepPart, - d => this.DecodeEncKdcRepPart(d) - ); + // Unwrap FAST response and strengthen the reply key if FAST was used + KerberosKey replyKey = null; + + if (this.UseFast && this.FastArmor?.ArmorKey != null) + { + replyKey = credential.CreateKey(); + var fastResponse = this.FastArmor.UnwrapResponse(asRep, ref replyKey); + + if (fastResponse != null) + { + this.logger.LogTrace("FAST response unwrapped successfully"); + } + } + + var decrypted = replyKey != null + ? DecryptKdcRepWithKey(asRep, replyKey) + : credential.DecryptKdcRep( + asRep, + KeyUsage.EncAsRepPart, + d => this.DecodeEncKdcRepPart(d) + ); VerifyNonces(asReqMessage.Body.Nonce, decrypted.Nonce); @@ -1363,6 +1413,15 @@ private async Task RequestTgt(KerberosCredential credential, KrbPrincipalName tg this.CacheTgt(asRep, decrypted); } + private KrbEncKdcRepPart DecryptKdcRepWithKey(KrbAsRep asRep, KerberosKey key) + { + return asRep.EncPart.Decrypt( + key, + KeyUsage.EncAsRepPart, + d => this.DecodeEncKdcRepPart(d) + ); + } + private KrbEncKdcRepPart DecodeEncKdcRepPart(ReadOnlyMemory decrypted) where T : KrbEncKdcRepPart, new() { diff --git a/Kerberos.NET/Configuration/Krb5KdcDefaults.cs b/Kerberos.NET/Configuration/Krb5KdcDefaults.cs index 1cd17a50..b060c06b 100644 --- a/Kerberos.NET/Configuration/Krb5KdcDefaults.cs +++ b/Kerberos.NET/Configuration/Krb5KdcDefaults.cs @@ -98,6 +98,14 @@ public class Krb5KdcDefaults : Krb5ConfigObject [DisplayName("kdc_register_pkinit")] public bool RegisterDefaultPkInitPreAuthHandler { get; set; } + /// + /// Indicates whether the KDC will automatically register the FAST (RFC 6113) + /// and Encrypted Challenge pre-auth handlers. + /// + [DefaultValue(true)] + [DisplayName("kdc_register_fast")] + public bool RegisterDefaultFastHandler { get; set; } + /// /// The amount of time the KDC should wait receiving a request before timing out. /// diff --git a/Kerberos.NET/Entities/Krb/KrbApRep.generated.cs b/Kerberos.NET/Entities/Krb/KrbApRep.generated.cs deleted file mode 100644 index 9642cd97..00000000 --- a/Kerberos.NET/Entities/Krb/KrbApRep.generated.cs +++ /dev/null @@ -1,189 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbApRep - { - /* - AP-REP ::= [APPLICATION 15] SEQUENCE { - pvno [0] INTEGER (5), - msg-type [1] INTEGER (15), - enc-part [2] EncryptedData - } - - EncAPRepPart ::= [APPLICATION 27] SEQUENCE { - ctime [0] KerberosTime, - cusec [1] Microseconds, - subkey [2] EncryptionKey OPTIONAL, - seq-number [3] UInt32 OPTIONAL - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public KrbEncryptedData EncryptedPart { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - EncryptedPart?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 15); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbApRep DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbApRep decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbApRep DecodeApplication(AsnReader reader, out T decoded) - where T: KrbApRep, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbApRep Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbApRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbApRep Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbApRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbApRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbApRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncryptedPart); - decoded.EncryptedPart = tmpEncryptedPart; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbApReq.generated.cs b/Kerberos.NET/Entities/Krb/KrbApReq.generated.cs deleted file mode 100644 index 2e4619cf..00000000 --- a/Kerberos.NET/Entities/Krb/KrbApReq.generated.cs +++ /dev/null @@ -1,213 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbApReq - { - /* - AP-REQ ::= [APPLICATION 14] SEQUENCE { - pvno [0] INTEGER (5), - msg-type [1] INTEGER (14), - ap-options [2] APOptions, - ticket [3] Ticket, - authenticator [4] EncryptedData - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public ApOptions ApOptions { get; set; } - public KrbTicket Ticket { get; set; } - - public KrbEncryptedData Authenticator { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteBitString(ApOptions.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - Ticket?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - Authenticator?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 14); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbApReq DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbApReq decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbApReq DecodeApplication(AsnReader reader, out T decoded) - where T: KrbApReq, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbApReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbApReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbApReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbApReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbApReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbApReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpApOptions)) - { - decoded.ApOptions = (ApOptions)tmpApOptions.AsLong(); - } - else - { - decoded.ApOptions = (ApOptions)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - KrbTicket.Decode(explicitReader, out KrbTicket tmpTicket); - decoded.Ticket = tmpTicket; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpAuthenticator); - decoded.Authenticator = tmpAuthenticator; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbAsRep.generated.cs b/Kerberos.NET/Entities/Krb/KrbAsRep.generated.cs deleted file mode 100644 index 6544ba9c..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAsRep.generated.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAsRep : KrbKdcRep - { - /* - AS-REP ::= [APPLICATION 11] KDC-REP - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 11); - - public override ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbAsRep DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbAsRep decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAsReq.generated.cs b/Kerberos.NET/Entities/Krb/KrbAsReq.generated.cs deleted file mode 100644 index 50ea6009..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAsReq.generated.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAsReq : KrbKdcReq - { - /* - AS-REQ ::= [APPLICATION 10] KDC-REQ - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 10); - - public override ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbAsReq DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbAsReq decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAuthenticator.generated.cs b/Kerberos.NET/Entities/Krb/KrbAuthenticator.generated.cs deleted file mode 100644 index 2a502ee7..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAuthenticator.generated.cs +++ /dev/null @@ -1,311 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAuthenticator - { - /* - Authenticator ::= [APPLICATION 2] SEQUENCE { - authenticator-vno [0] INTEGER (5), - crealm [1] Realm, - cname [2] PrincipalName, - cksum [3] Checksum OPTIONAL, - cusec [4] Microseconds, - ctime [5] KerberosTime, - subkey [6] EncryptionKey OPTIONAL, - seq-number [7] UInt32 OPTIONAL, - authorization-data [8] AuthorizationData OPTIONAL - } - */ - - public int AuthenticatorVersionNumber { get; set; } - - public string CRealm { get; set; } - - public KrbPrincipalName CName { get; set; } - - public KrbChecksum Checksum { get; set; } - - public int CuSec { get; set; } - - public DateTimeOffset CTime { get; set; } - - public KrbEncryptionKey Subkey { get; set; } - - public int? SequenceNumber { get; set; } - - public KrbAuthorizationData[] AuthorizationData { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(AuthenticatorVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, CRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (Asn1Extension.HasValue(Checksum)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - Checksum?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteInteger(CuSec); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.WriteGeneralizedTime(CTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - if (Asn1Extension.HasValue(Subkey)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - Subkey?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - } - - if (Asn1Extension.HasValue(SequenceNumber)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.WriteInteger(SequenceNumber.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - } - - if (Asn1Extension.HasValue(AuthorizationData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - writer.PushSequence(); - - for (int i = 0; i < AuthorizationData.Length; i++) - { - AuthorizationData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - } - - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 2); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbAuthenticator DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbAuthenticator decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbAuthenticator DecodeApplication(AsnReader reader, out T decoded) - where T: KrbAuthenticator, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbAuthenticator Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbAuthenticator decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbAuthenticator Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbAuthenticator decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbAuthenticator, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbAuthenticator, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpAuthenticatorVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.AuthenticatorVersionNumber = tmpAuthenticatorVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.CRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - KrbChecksum.Decode(explicitReader, out KrbChecksum tmpChecksum); - decoded.Checksum = tmpChecksum; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - if (!explicitReader.TryReadInt32(out int tmpCuSec)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.CuSec = tmpCuSec; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - decoded.CTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - KrbEncryptionKey.Decode(explicitReader, out KrbEncryptionKey tmpSubkey); - decoded.Subkey = tmpSubkey; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - - if (explicitReader.TryReadInt32(out int tmpSequenceNumber)) - { - decoded.SequenceNumber = tmpSequenceNumber; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - - // Decode SEQUENCE OF for AuthorizationData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbAuthorizationData tmpItem; - - while (collectionReader.HasData) - { - KrbAuthorizationData.Decode(collectionReader, out KrbAuthorizationData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.AuthorizationData = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbAuthorizationData.generated.cs b/Kerberos.NET/Entities/Krb/KrbAuthorizationData.generated.cs deleted file mode 100644 index f9c899c9..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAuthorizationData.generated.cs +++ /dev/null @@ -1,158 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAuthorizationData - { - /* - AuthorizationData ::= SEQUENCE OF SEQUENCE { - ad-type [0] Int32, - ad-data [1] OCTET STRING - } - */ - - public AuthorizationDataType Type { get; set; } - - public ReadOnlyMemory Data { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(Data.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbAuthorizationData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbAuthorizationData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbAuthorizationData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbAuthorizationData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbAuthorizationData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbAuthorizationData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbAuthorizationData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbAuthorizationData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out AuthorizationDataType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpData)) - { - decoded.Data = tmpData; - } - else - { - decoded.Data = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbChecksum.generated.cs b/Kerberos.NET/Entities/Krb/KrbChecksum.generated.cs deleted file mode 100644 index ca847002..00000000 --- a/Kerberos.NET/Entities/Krb/KrbChecksum.generated.cs +++ /dev/null @@ -1,158 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbChecksum - { - /* - Checksum ::= SEQUENCE { - cksumtype [0] Int32, - checksum [1] OCTET STRING - } - */ - - public ChecksumType Type { get; set; } - - public ReadOnlyMemory Checksum { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1 )); - writer.WriteOctetString(Checksum.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1 )); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbChecksum Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbChecksum Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbChecksum Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbChecksum decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbChecksum Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbChecksum decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbChecksum, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbChecksum, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out ChecksumType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1 )); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpChecksum)) - { - decoded.Checksum = tmpChecksum; - } - else - { - decoded.Checksum = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbCred.generated.cs b/Kerberos.NET/Entities/Krb/KrbCred.generated.cs deleted file mode 100644 index 9c00d248..00000000 --- a/Kerberos.NET/Entities/Krb/KrbCred.generated.cs +++ /dev/null @@ -1,217 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbCred - { - /* - KRB-CRED ::= [APPLICATION 22] SEQUENCE { - pvno [0] INTEGER (5), - msg-type [1] INTEGER (22), - tickets [2] SEQUENCE OF Ticket, - enc-part [3] EncryptedData - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public KrbTicket[] Tickets { get; set; } - - public KrbEncryptedData EncryptedPart { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(); - - for (int i = 0; i < Tickets.Length; i++) - { - Tickets[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - EncryptedPart?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 22); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbCred DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbCred decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbCred DecodeApplication(AsnReader reader, out T decoded) - where T: KrbCred, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbCred Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbCred decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbCred Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbCred decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbCred, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbCred, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - // Decode SEQUENCE OF for Tickets - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbTicket tmpItem; - - while (collectionReader.HasData) - { - KrbTicket.Decode(collectionReader, out KrbTicket tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.Tickets = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncryptedPart); - decoded.EncryptedPart = tmpEncryptedPart; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbCredInfo.generated.cs b/Kerberos.NET/Entities/Krb/KrbCredInfo.generated.cs deleted file mode 100644 index a2823532..00000000 --- a/Kerberos.NET/Entities/Krb/KrbCredInfo.generated.cs +++ /dev/null @@ -1,344 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbCredInfo - { - /* - KrbCredInfo ::= SEQUENCE { - key [0] EncryptionKey, - prealm [1] Realm OPTIONAL, - pname [2] PrincipalName OPTIONAL, - flags [3] TicketFlags OPTIONAL, - authtime [4] KerberosTime OPTIONAL, - starttime [5] KerberosTime OPTIONAL, - endtime [6] KerberosTime OPTIONAL, - renew-till [7] KerberosTime OPTIONAL, - srealm [8] Realm OPTIONAL, - sname [9] PrincipalName OPTIONAL, - caddr [10] HostAddresses OPTIONAL - } - */ - - public KrbEncryptionKey Key { get; set; } - - public string Realm { get; set; } - - public KrbPrincipalName PName { get; set; } - - public TicketFlags Flags { get; set; } - public DateTimeOffset? AuthTime { get; set; } - - public DateTimeOffset? StartTime { get; set; } - - public DateTimeOffset? EndTime { get; set; } - - public DateTimeOffset? RenewTill { get; set; } - - public string SRealm { get; set; } - - public KrbPrincipalName SName { get; set; } - - public KrbAuthorizationData[] AuthorizationData { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - Key?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(Realm)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Realm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - - if (Asn1Extension.HasValue(PName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - PName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteBitString(Flags.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (Asn1Extension.HasValue(AuthTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteGeneralizedTime(AuthTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - } - - if (Asn1Extension.HasValue(StartTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.WriteGeneralizedTime(StartTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - } - - if (Asn1Extension.HasValue(EndTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - writer.WriteGeneralizedTime(EndTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - } - - if (Asn1Extension.HasValue(RenewTill)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.WriteGeneralizedTime(RenewTill.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - } - - if (Asn1Extension.HasValue(SRealm)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, SRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - } - - - if (Asn1Extension.HasValue(SName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - SName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - } - - if (Asn1Extension.HasValue(AuthorizationData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - writer.PushSequence(); - - for (int i = 0; i < AuthorizationData.Length; i++) - { - AuthorizationData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - } - - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbCredInfo Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbCredInfo Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbCredInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbCredInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbCredInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbCredInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbCredInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbCredInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbEncryptionKey.Decode(explicitReader, out KrbEncryptionKey tmpKey); - decoded.Key = tmpKey; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - decoded.Realm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpPName); - decoded.PName = tmpPName; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpFlags)) - { - decoded.Flags = (TicketFlags)tmpFlags.AsLong(); - } - else - { - decoded.Flags = (TicketFlags)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - decoded.AuthTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - decoded.StartTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - decoded.EndTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - - decoded.RenewTill = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - - decoded.SRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 9))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpSName); - decoded.SName = tmpSName; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 10))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - - // Decode SEQUENCE OF for AuthorizationData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbAuthorizationData tmpItem; - - while (collectionReader.HasData) - { - KrbAuthorizationData.Decode(collectionReader, out KrbAuthorizationData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.AuthorizationData = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.generated.cs b/Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.generated.cs deleted file mode 100644 index 8e8810c3..00000000 --- a/Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.generated.cs +++ /dev/null @@ -1,186 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbETypeInfo2Entry - { - /* - ETYPE-INFO2-ENTRY ::= SEQUENCE { - etype [0] Int32, - salt [1] KerberosString OPTIONAL, - s2kparams [2] OCTET STRING OPTIONAL - } - - ETYPE-INFO2 ::= SEQUENCE SIZE (1..MAX) OF ETYPE-INFO2-ENTRY - */ - - public EncryptionType EType { get; set; } - - public string Salt { get; set; } - - public ReadOnlyMemory? S2kParams { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)EType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(Salt)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Salt); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - - if (Asn1Extension.HasValue(S2kParams)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(S2kParams.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbETypeInfo2Entry Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbETypeInfo2Entry Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbETypeInfo2Entry Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbETypeInfo2Entry decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbETypeInfo2Entry Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbETypeInfo2Entry decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbETypeInfo2Entry, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbETypeInfo2Entry, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out EncryptionType tmpEType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.EType = tmpEType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - decoded.Salt = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpS2kParams)) - { - decoded.S2kParams = tmpS2kParams; - } - else - { - decoded.S2kParams = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncApRepPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncApRepPart.generated.cs deleted file mode 100644 index 0f209092..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncApRepPart.generated.cs +++ /dev/null @@ -1,209 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncApRepPart - { - /* - EncAPRepPart ::= [APPLICATION 27] SEQUENCE { - ctime [0] KerberosTime, - cusec [1] Microseconds, - subkey [2] EncryptionKey OPTIONAL, - seq-number [3] UInt32 OPTIONAL - } - */ - - public DateTimeOffset CTime { get; set; } - - public int CuSec { get; set; } - - public KrbEncryptionKey SubSessionKey { get; set; } - - public int? SequenceNumber { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteGeneralizedTime(CTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(CuSec); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (Asn1Extension.HasValue(SubSessionKey)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - SubSessionKey?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(SequenceNumber)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteInteger(SequenceNumber.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 27); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbEncApRepPart DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbEncApRepPart decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbEncApRepPart DecodeApplication(AsnReader reader, out T decoded) - where T: KrbEncApRepPart, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbEncApRepPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncApRepPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncApRepPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncApRepPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncApRepPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncApRepPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - decoded.CTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out int tmpCuSec)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.CuSec = tmpCuSec; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - KrbEncryptionKey.Decode(explicitReader, out KrbEncryptionKey tmpSubSessionKey); - decoded.SubSessionKey = tmpSubSessionKey; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (explicitReader.TryReadInt32(out int tmpSequenceNumber)) - { - decoded.SequenceNumber = tmpSequenceNumber; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncAsRepPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncAsRepPart.generated.cs deleted file mode 100644 index 1180926b..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncAsRepPart.generated.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncAsRepPart : KrbEncKdcRepPart - { - /* - EncASRepPart ::= [APPLICATION 25] EncKDCRepPart - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 25); - - public override ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbEncAsRepPart DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbEncAsRepPart decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.generated.cs deleted file mode 100644 index f356f85d..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.generated.cs +++ /dev/null @@ -1,363 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncKdcRepPart - { - /* - EncKDCRepPart ::= SEQUENCE { - key [0] EncryptionKey, - last-req [1] LastReq, - nonce [2] UInt32, - key-expiration [3] KerberosTime OPTIONAL, - flags [4] TicketFlags, - authtime [5] KerberosTime, - starttime [6] KerberosTime OPTIONAL, - endtime [7] KerberosTime, - renew-till [8] KerberosTime OPTIONAL, - srealm [9] Realm, - sname [10] PrincipalName, - caddr [11] HostAddresses OPTIONAL - } - */ - - public KrbEncryptionKey Key { get; set; } - - public KrbLastReq[] LastReq { get; set; } - - public int Nonce { get; set; } - - public DateTimeOffset? KeyExpiration { get; set; } - - public TicketFlags Flags { get; set; } - public DateTimeOffset AuthTime { get; set; } - - public DateTimeOffset? StartTime { get; set; } - - public DateTimeOffset EndTime { get; set; } - - public DateTimeOffset? RenewTill { get; set; } - - public string Realm { get; set; } - - public KrbPrincipalName SName { get; set; } - - public KrbHostAddress[] CAddr { get; set; } - - public KrbMethodData EncryptedPaData { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - Key?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(); - - for (int i = 0; i < LastReq.Length; i++) - { - LastReq[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteInteger(Nonce); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (Asn1Extension.HasValue(KeyExpiration)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteGeneralizedTime(KeyExpiration.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteBitString(Flags.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.WriteGeneralizedTime(AuthTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - if (Asn1Extension.HasValue(StartTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - writer.WriteGeneralizedTime(StartTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.WriteGeneralizedTime(EndTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - - if (Asn1Extension.HasValue(RenewTill)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - writer.WriteGeneralizedTime(RenewTill.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Realm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - SName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - - if (Asn1Extension.HasValue(CAddr)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - writer.PushSequence(); - - for (int i = 0; i < CAddr.Length; i++) - { - CAddr[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - } - - - if (Asn1Extension.HasValue(EncryptedPaData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 12)); - EncryptedPaData?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 12)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbEncKdcRepPart Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbEncKdcRepPart Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbEncKdcRepPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncKdcRepPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncKdcRepPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncKdcRepPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncKdcRepPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncKdcRepPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbEncryptionKey.Decode(explicitReader, out KrbEncryptionKey tmpKey); - decoded.Key = tmpKey; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - // Decode SEQUENCE OF for LastReq - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbLastReq tmpItem; - - while (collectionReader.HasData) - { - KrbLastReq.Decode(collectionReader, out KrbLastReq tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.LastReq = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (!explicitReader.TryReadInt32(out int tmpNonce)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Nonce = tmpNonce; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - decoded.KeyExpiration = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpFlags)) - { - decoded.Flags = (TicketFlags)tmpFlags.AsLong(); - } - else - { - decoded.Flags = (TicketFlags)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - decoded.AuthTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - decoded.StartTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - decoded.EndTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - - decoded.RenewTill = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - decoded.Realm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpSName); - decoded.SName = tmpSName; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 11))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - - // Decode SEQUENCE OF for CAddr - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbHostAddress tmpItem; - - while (collectionReader.HasData) - { - KrbHostAddress.Decode(collectionReader, out KrbHostAddress tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.CAddr = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 12))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 12)); - - KrbMethodData.Decode(explicitReader, out KrbMethodData tmpEncryptedPaData); - decoded.EncryptedPaData = tmpEncryptedPaData; - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.generated.cs deleted file mode 100644 index f3bc9be0..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.generated.cs +++ /dev/null @@ -1,279 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncKrbCredPart - { - /* - EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { - ticket-info [0] SEQUENCE OF KrbCredInfo, - nonce [1] UInt32 OPTIONAL, - timestamp [2] KerberosTime OPTIONAL, - usec [3] Microseconds OPTIONAL, - s-address [4] HostAddress OPTIONAL, - r-address [5] HostAddress OPTIONAL - } - */ - - public KrbCredInfo[] TicketInfo { get; set; } - - public int? Nonce { get; set; } - - public DateTimeOffset? Timestamp { get; set; } - - public int? USec { get; set; } - - public KrbHostAddress SAddress { get; set; } - - public KrbHostAddress RAddress { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(); - - for (int i = 0; i < TicketInfo.Length; i++) - { - TicketInfo[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(Nonce)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(Nonce.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(Timestamp)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteGeneralizedTime(Timestamp.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(USec)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteInteger(USec.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - - if (Asn1Extension.HasValue(SAddress)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - SAddress?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - } - - if (Asn1Extension.HasValue(RAddress)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - RAddress?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 29); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbEncKrbCredPart DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbEncKrbCredPart decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbEncKrbCredPart DecodeApplication(AsnReader reader, out T decoded) - where T: KrbEncKrbCredPart, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbEncKrbCredPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncKrbCredPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncKrbCredPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncKrbCredPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncKrbCredPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncKrbCredPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - // Decode SEQUENCE OF for TicketInfo - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbCredInfo tmpItem; - - while (collectionReader.HasData) - { - KrbCredInfo.Decode(collectionReader, out KrbCredInfo tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.TicketInfo = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadInt32(out int tmpNonce)) - { - decoded.Nonce = tmpNonce; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - decoded.Timestamp = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (explicitReader.TryReadInt32(out int tmpUSec)) - { - decoded.USec = tmpUSec; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - KrbHostAddress.Decode(explicitReader, out KrbHostAddress tmpSAddress); - decoded.SAddress = tmpSAddress; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - KrbHostAddress.Decode(explicitReader, out KrbHostAddress tmpRAddress); - decoded.RAddress = tmpRAddress; - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.generated.cs deleted file mode 100644 index a8dd83ea..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.generated.cs +++ /dev/null @@ -1,256 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncKrbPrivPart - { - /* - EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE { - user-data [0] OCTET STRING, - timestamp [1] KerberosTime OPTIONAL, - usec [2] Microseconds OPTIONAL, - seq-number [3] UInt32 OPTIONAL, - s-address [4] HostAddress ( sender's addr ), - r-address [5] HostAddress OPTIONAL ( recip's addr ) - } - */ - - public ReadOnlyMemory UserData { get; set; } - - public DateTimeOffset? Timestamp { get; set; } - - public int? Usec { get; set; } - - public int? SeqNumber { get; set; } - - public KrbHostAddress SAddress { get; set; } - - public KrbHostAddress RAddress { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteOctetString(UserData.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(Timestamp)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteGeneralizedTime(Timestamp.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(Usec)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteInteger(Usec.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(SeqNumber)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteInteger(SeqNumber.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - SAddress?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - if (Asn1Extension.HasValue(RAddress)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - RAddress?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 28); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbEncKrbPrivPart DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbEncKrbPrivPart decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbEncKrbPrivPart DecodeApplication(AsnReader reader, out T decoded) - where T: KrbEncKrbPrivPart, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbEncKrbPrivPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncKrbPrivPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncKrbPrivPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncKrbPrivPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncKrbPrivPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncKrbPrivPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpUserData)) - { - decoded.UserData = tmpUserData; - } - else - { - decoded.UserData = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - decoded.Timestamp = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (explicitReader.TryReadInt32(out int tmpUsec)) - { - decoded.Usec = tmpUsec; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (explicitReader.TryReadInt32(out int tmpSeqNumber)) - { - decoded.SeqNumber = tmpSeqNumber; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - KrbHostAddress.Decode(explicitReader, out KrbHostAddress tmpSAddress); - decoded.SAddress = tmpSAddress; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - KrbHostAddress.Decode(explicitReader, out KrbHostAddress tmpRAddress); - decoded.RAddress = tmpRAddress; - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.generated.cs deleted file mode 100644 index cac9e263..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.generated.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncTgsRepPart : KrbEncKdcRepPart - { - /* - EncTGSRepPart ::= [APPLICATION 26] EncKDCRepPart - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 26); - - public override ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbEncTgsRepPart DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbEncTgsRepPart decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncTicketPart.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncTicketPart.generated.cs deleted file mode 100644 index 9695ed89..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncTicketPart.generated.cs +++ /dev/null @@ -1,344 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncTicketPart - { - /* - EncTicketPart ::= [APPLICATION 3] SEQUENCE { - flags [0] TicketFlags, - key [1] EncryptionKey, - crealm [2] Realm, - cname [3] PrincipalName, - transited [4] TransitedEncoding, - authtime [5] KerberosTime, - starttime [6] KerberosTime OPTIONAL, - endtime [7] KerberosTime, - renew-till [8] KerberosTime OPTIONAL, - caddr [9] HostAddresses OPTIONAL, - authorization-data [10] AuthorizationData OPTIONAL - } - */ - - public TicketFlags Flags { get; set; } - public KrbEncryptionKey Key { get; set; } - - public string CRealm { get; set; } - - public KrbPrincipalName CName { get; set; } - - public KrbTransitedEncoding Transited { get; set; } - - public DateTimeOffset AuthTime { get; set; } - - public DateTimeOffset? StartTime { get; set; } - - public DateTimeOffset EndTime { get; set; } - - public DateTimeOffset? RenewTill { get; set; } - - public KrbHostAddress[] CAddr { get; set; } - - public KrbAuthorizationData[] AuthorizationData { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteBitString(Flags.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - Key?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, CRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - Transited?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.WriteGeneralizedTime(AuthTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - if (Asn1Extension.HasValue(StartTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - writer.WriteGeneralizedTime(StartTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.WriteGeneralizedTime(EndTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - - if (Asn1Extension.HasValue(RenewTill)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - writer.WriteGeneralizedTime(RenewTill.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - } - - if (Asn1Extension.HasValue(CAddr)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - writer.PushSequence(); - - for (int i = 0; i < CAddr.Length; i++) - { - CAddr[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - } - - - if (Asn1Extension.HasValue(AuthorizationData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - writer.PushSequence(); - - for (int i = 0; i < AuthorizationData.Length; i++) - { - AuthorizationData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - } - - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 3); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbEncTicketPart DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbEncTicketPart decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbEncTicketPart DecodeApplication(AsnReader reader, out T decoded) - where T: KrbEncTicketPart, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbEncTicketPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncTicketPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncTicketPart Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncTicketPart decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncTicketPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncTicketPart, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpFlags)) - { - decoded.Flags = (TicketFlags)tmpFlags.AsLong(); - } - else - { - decoded.Flags = (TicketFlags)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - KrbEncryptionKey.Decode(explicitReader, out KrbEncryptionKey tmpKey); - decoded.Key = tmpKey; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - decoded.CRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - KrbTransitedEncoding.Decode(explicitReader, out KrbTransitedEncoding tmpTransited); - decoded.Transited = tmpTransited; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - decoded.AuthTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - decoded.StartTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - decoded.EndTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - - decoded.RenewTill = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 9))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - - // Decode SEQUENCE OF for CAddr - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbHostAddress tmpItem; - - while (collectionReader.HasData) - { - KrbHostAddress.Decode(collectionReader, out KrbHostAddress tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.CAddr = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 10))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - - // Decode SEQUENCE OF for AuthorizationData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbAuthorizationData tmpItem; - - while (collectionReader.HasData) - { - KrbAuthorizationData.Decode(collectionReader, out KrbAuthorizationData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.AuthorizationData = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncryptedData.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncryptedData.generated.cs deleted file mode 100644 index e953a098..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncryptedData.generated.cs +++ /dev/null @@ -1,184 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncryptedData - { - /* - EncryptedData ::= SEQUENCE { - etype [0] Int32, - kvno [1] UInt32 OPTIONAL, - cipher [2] OCTET STRING - } - */ - - public EncryptionType EType { get; set; } - - public int? KeyVersionNumber { get; set; } - - public ReadOnlyMemory Cipher { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)EType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(KeyVersionNumber)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(KeyVersionNumber.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2 )); - writer.WriteOctetString(Cipher.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2 )); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbEncryptedData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbEncryptedData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbEncryptedData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncryptedData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncryptedData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncryptedData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncryptedData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncryptedData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out EncryptionType tmpEType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.EType = tmpEType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadInt32(out int tmpKeyVersionNumber)) - { - decoded.KeyVersionNumber = tmpKeyVersionNumber; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2 )); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpCipher)) - { - decoded.Cipher = tmpCipher; - } - else - { - decoded.Cipher = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbEncryptionKey.generated.cs b/Kerberos.NET/Entities/Krb/KrbEncryptionKey.generated.cs deleted file mode 100644 index c9bc99d7..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncryptionKey.generated.cs +++ /dev/null @@ -1,158 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbEncryptionKey - { - /* - EncryptionKey ::= SEQUENCE { - keytype [0] Int32, - keyvalue [1] OCTET STRING - } - */ - - public EncryptionType EType { get; set; } - - public ReadOnlyMemory KeyValue { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)EType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1 )); - writer.WriteOctetString(KeyValue.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1 )); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbEncryptionKey Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbEncryptionKey Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbEncryptionKey Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbEncryptionKey decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbEncryptionKey Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbEncryptionKey decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbEncryptionKey, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbEncryptionKey, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out EncryptionType tmpEType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.EType = tmpEType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1 )); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpKeyValue)) - { - decoded.KeyValue = tmpKeyValue; - } - else - { - decoded.KeyValue = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbError.generated.cs b/Kerberos.NET/Entities/Krb/KrbError.generated.cs deleted file mode 100644 index 44053e71..00000000 --- a/Kerberos.NET/Entities/Krb/KrbError.generated.cs +++ /dev/null @@ -1,365 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbError - { - /* - KRB-ERROR ::= [APPLICATION 30] SEQUENCE { - pvno [0] INTEGER (5), - msg-type [1] INTEGER (30), - ctime [2] KerberosTime OPTIONAL, - cusec [3] Microseconds OPTIONAL, - stime [4] KerberosTime, - susec [5] Microseconds, - error-code [6] Int32, - crealm [7] Realm OPTIONAL, - cname [8] PrincipalName OPTIONAL, - realm [9] Realm , - sname [10] PrincipalName, - e-text [11] KerberosString OPTIONAL, - e-data [12] OCTET STRING OPTIONAL - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public DateTimeOffset? CTime { get; set; } - - public int? Cusec { get; set; } - - public DateTimeOffset STime { get; set; } - - public int Susc { get; set; } - - public KerberosErrorCode ErrorCode { get; set; } - - public string CRealm { get; set; } - - public KrbPrincipalName CName { get; set; } - - public string Realm { get; set; } - - public KrbPrincipalName SName { get; set; } - - public string EText { get; set; } - - public ReadOnlyMemory? EData { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (Asn1Extension.HasValue(CTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteGeneralizedTime(CTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(Cusec)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteInteger(Cusec.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteGeneralizedTime(STime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.WriteInteger(Susc); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - writer.WriteInteger((long)ErrorCode); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - if (Asn1Extension.HasValue(CRealm)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, CRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - } - - - if (Asn1Extension.HasValue(CName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Realm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - SName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - - if (Asn1Extension.HasValue(EText)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, EText); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - } - - - if (Asn1Extension.HasValue(EData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 12)); - writer.WriteOctetString(EData.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 12)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 30); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbError DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbError decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbError DecodeApplication(AsnReader reader, out T decoded) - where T: KrbError, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbError Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbError decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbError Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbError decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbError, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbError, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - decoded.CTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (explicitReader.TryReadInt32(out int tmpCusec)) - { - decoded.Cusec = tmpCusec; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - decoded.STime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - if (!explicitReader.TryReadInt32(out int tmpSusc)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Susc = tmpSusc; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - if (!explicitReader.TryReadInt32(out KerberosErrorCode tmpErrorCode)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ErrorCode = tmpErrorCode; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - - decoded.CRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - decoded.Realm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpSName); - decoded.SName = tmpSName; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 11))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - - decoded.EText = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 12))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 12)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpEData)) - { - decoded.EData = tmpEData; - } - else - { - decoded.EData = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbHostAddress.generated.cs b/Kerberos.NET/Entities/Krb/KrbHostAddress.generated.cs deleted file mode 100644 index 0f64015e..00000000 --- a/Kerberos.NET/Entities/Krb/KrbHostAddress.generated.cs +++ /dev/null @@ -1,161 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbHostAddress - { - /* - HostAddress ::= SEQUENCE { - addr-type [0] Int32, - address [1] OCTET STRING - } - - HostAddresses - ::= SEQUENCE OF HostAddress - */ - - public AddressType AddressType { get; set; } - - public ReadOnlyMemory Address { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)AddressType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(Address.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbHostAddress Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbHostAddress Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbHostAddress Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbHostAddress decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbHostAddress Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbHostAddress decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbHostAddress, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbHostAddress, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out AddressType tmpAddressType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.AddressType = tmpAddressType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpAddress)) - { - decoded.Address = tmpAddress; - } - else - { - decoded.Address = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbKdcRep.generated.cs b/Kerberos.NET/Entities/Krb/KrbKdcRep.generated.cs deleted file mode 100644 index 85b0dba9..00000000 --- a/Kerberos.NET/Entities/Krb/KrbKdcRep.generated.cs +++ /dev/null @@ -1,250 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbKdcRep - { - /* - AS-REP ::= [APPLICATION 11] KDC-REP - - TGS-REP ::= [APPLICATION 13] KDC-REP - - KDC-REP ::= SEQUENCE { - pvno [0] INTEGER (5), - msg-type [1] INTEGER, - padata [2] SEQUENCE OF PA-DATA OPTIONAL, - crealm [3] Realm, - cname [4] PrincipalName, - ticket [5] Ticket, - enc-part [6] EncryptedData - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public KrbPaData[] PaData { get; set; } - - public string CRealm { get; set; } - - public KrbPrincipalName CName { get; set; } - - public KrbTicket Ticket { get; set; } - - public KrbEncryptedData EncPart { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (Asn1Extension.HasValue(PaData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(); - - for (int i = 0; i < PaData.Length; i++) - { - PaData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, CRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - Ticket?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - EncPart?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbKdcRep Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbKdcRep Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbKdcRep Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbKdcRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbKdcRep Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbKdcRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbKdcRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbKdcRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - // Decode SEQUENCE OF for PaData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbPaData tmpItem; - - while (collectionReader.HasData) - { - KrbPaData.Decode(collectionReader, out KrbPaData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.PaData = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - decoded.CRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - KrbTicket.Decode(explicitReader, out KrbTicket tmpTicket); - decoded.Ticket = tmpTicket; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncPart); - decoded.EncPart = tmpEncPart; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbKdcReq.generated.cs b/Kerberos.NET/Entities/Krb/KrbKdcReq.generated.cs deleted file mode 100644 index 84cf5864..00000000 --- a/Kerberos.NET/Entities/Krb/KrbKdcReq.generated.cs +++ /dev/null @@ -1,211 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbKdcReq - { - /* - KDC-REQ ::= SEQUENCE { - pvno [1] INTEGER (5) , - msg-type [2] INTEGER, - padata [3] SEQUENCE OF PA-DATA OPTIONAL - req-body [4] KDC-REQ-BODY - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public KrbPaData[] PaData { get; set; } - - public KrbKdcReqBody Body { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (Asn1Extension.HasValue(PaData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PushSequence(); - - for (int i = 0; i < PaData.Length; i++) - { - PaData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - Body?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbKdcReq Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbKdcReq Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbKdcReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbKdcReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbKdcReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbKdcReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbKdcReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbKdcReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - // Decode SEQUENCE OF for PaData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbPaData tmpItem; - - while (collectionReader.HasData) - { - KrbPaData.Decode(collectionReader, out KrbPaData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.PaData = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - KrbKdcReqBody.Decode(explicitReader, out KrbKdcReqBody tmpBody); - decoded.Body = tmpBody; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbKdcReqBody.generated.cs b/Kerberos.NET/Entities/Krb/KrbKdcReqBody.generated.cs deleted file mode 100644 index 38f35af1..00000000 --- a/Kerberos.NET/Entities/Krb/KrbKdcReqBody.generated.cs +++ /dev/null @@ -1,395 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbKdcReqBody - { - /* - KDC-REQ-BODY ::= SEQUENCE { - kdc-options [0] KDCOptions, - cname [1] PrincipalName OPTIONAL - realm [2] Realm - sname [3] PrincipalName OPTIONAL, - from [4] KerberosTime OPTIONAL, - till [5] KerberosTime, - rtime [6] KerberosTime OPTIONAL, - nonce [7] UInt32, - etype [8] SEQUENCE OF Int32 - addresses [9] HostAddresses OPTIONAL, - enc-authorization-data [10] EncryptedData OPTIONAL - additional-tickets [11] SEQUENCE OF Ticket OPTIONAL - } - */ - - public KdcOptions KdcOptions { get; set; } - public KrbPrincipalName CName { get; set; } - - public string Realm { get; set; } - - public KrbPrincipalName SName { get; set; } - - public DateTimeOffset? From { get; set; } - - public DateTimeOffset Till { get; set; } - - public DateTimeOffset? RTime { get; set; } - - public int Nonce { get; set; } - - public EncryptionType[] EType { get; set; } - - public KrbHostAddress[] Addresses { get; set; } - - public KrbEncryptedData EncAuthorizationData { get; set; } - - public KrbTicket[] AdditionalTickets { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteBitString(KdcOptions.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(CName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Realm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (Asn1Extension.HasValue(SName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - SName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - - if (Asn1Extension.HasValue(From)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteGeneralizedTime(From.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - writer.WriteGeneralizedTime(Till); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - - if (Asn1Extension.HasValue(RTime)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - writer.WriteGeneralizedTime(RTime.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.WriteInteger(Nonce); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - writer.PushSequence(); - - for (int i = 0; i < EType.Length; i++) - { - writer.WriteInteger((long)EType[i]); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - - if (Asn1Extension.HasValue(Addresses)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - writer.PushSequence(); - - for (int i = 0; i < Addresses.Length; i++) - { - Addresses[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - } - - - if (Asn1Extension.HasValue(EncAuthorizationData)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - EncAuthorizationData?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - } - - if (Asn1Extension.HasValue(AdditionalTickets)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - writer.PushSequence(); - - for (int i = 0; i < AdditionalTickets.Length; i++) - { - AdditionalTickets[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - } - - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbKdcReqBody Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbKdcReqBody Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbKdcReqBody Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbKdcReqBody decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbKdcReqBody Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbKdcReqBody decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbKdcReqBody, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbKdcReqBody, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpKdcOptions)) - { - decoded.KdcOptions = (KdcOptions)tmpKdcOptions.AsLong(); - } - else - { - decoded.KdcOptions = (KdcOptions)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - decoded.Realm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpSName); - decoded.SName = tmpSName; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - decoded.From = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 5)); - decoded.Till = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 6)); - - decoded.RTime = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 7)); - - if (!explicitReader.TryReadInt32(out int tmpNonce)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Nonce = tmpNonce; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 8)); - // Decode SEQUENCE OF for EType - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - EncryptionType tmpItem; - - while (collectionReader.HasData) - { - - if (!collectionReader.TryReadInt32(out EncryptionType tmp)) - { - collectionReader.ThrowIfNotEmpty(); - } - - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.EType = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 9))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 9)); - - // Decode SEQUENCE OF for Addresses - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbHostAddress tmpItem; - - while (collectionReader.HasData) - { - KrbHostAddress.Decode(collectionReader, out KrbHostAddress tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.Addresses = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 10))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 10)); - - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncAuthorizationData); - decoded.EncAuthorizationData = tmpEncAuthorizationData; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 11))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 11)); - - // Decode SEQUENCE OF for AdditionalTickets - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbTicket tmpItem; - - while (collectionReader.HasData) - { - KrbTicket.Decode(collectionReader, out KrbTicket tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.AdditionalTickets = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbLastReq.generated.cs b/Kerberos.NET/Entities/Krb/KrbLastReq.generated.cs deleted file mode 100644 index 7cafacd6..00000000 --- a/Kerberos.NET/Entities/Krb/KrbLastReq.generated.cs +++ /dev/null @@ -1,150 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbLastReq - { - /* - LastReq ::= SEQUENCE OF SEQUENCE { - lr-type [0] Int32, - lr-value [1] KerberosTime - } - */ - - public int Type { get; set; } - - public DateTimeOffset Value { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteGeneralizedTime(Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbLastReq Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbLastReq Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbLastReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbLastReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbLastReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbLastReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbLastReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbLastReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.Value = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaData.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaData.generated.cs deleted file mode 100644 index 645e2e49..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaData.generated.cs +++ /dev/null @@ -1,158 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaData - { - /* - PA-DATA ::= SEQUENCE { - padata-type [1] Int32, - padata-value [2] OCTET STRING - } - */ - - public PaDataType Type { get; set; } - - public ReadOnlyMemory Value { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out PaDataType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpValue)) - { - decoded.Value = tmpValue; - } - else - { - decoded.Value = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.generated.cs deleted file mode 100644 index 94ea8cc8..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.generated.cs +++ /dev/null @@ -1,159 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaEncTsEnc - { - /* - PA-ENC-TS-ENC ::= SEQUENCE { - patimestamp [0] KerberosTime, - pausec [1] Microseconds OPTIONAL - } - */ - - public DateTimeOffset PaTimestamp { get; set; } - - public int? PaUSec { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteGeneralizedTime(PaTimestamp); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(PaUSec)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(PaUSec.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaEncTsEnc Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaEncTsEnc Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaEncTsEnc Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaEncTsEnc decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaEncTsEnc Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaEncTsEnc decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaEncTsEnc, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaEncTsEnc, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - decoded.PaTimestamp = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadInt32(out int tmpPaUSec)) - { - decoded.PaUSec = tmpPaUSec; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs index f5105d29..6818c760 100644 --- a/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs +++ b/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs @@ -1,6 +1,10 @@ -// This is a generated file. -// This file is licensed as per the LICENSE file. -// The generation template has been modified from .NET Foundation implementation +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +// This is a generated file. +// The generation template has been modified from .NET Runtime implementation using System; using System.Security.Cryptography; @@ -12,9 +16,20 @@ namespace Kerberos.NET.Entities { public partial class KrbPaSvrReferralData { - public KrbPrincipalName ReferredName; - public string ReferredRealm; - + /* + PA-SVR-REFERRAL-INFO 20 + + PA-SVR-REFERRAL-DATA ::= SEQUENCE { + referred-name [1] PrincipalName OPTIONAL, + referred-realm [0] Realm + } + */ + + public KrbPrincipalName ReferredName { get; set; } + + public string ReferredRealm { get; set; } + + // Encoding methods public ReadOnlyMemory Encode() { var writer = new AsnWriter(AsnEncodingRules.DER); @@ -23,7 +38,7 @@ public ReadOnlyMemory Encode() return writer.EncodeAsMemory(); } - + internal void Encode(AsnWriter writer) { Encode(writer, Asn1Tag.Sequence); @@ -40,7 +55,6 @@ internal void Encode(AsnWriter writer, Asn1Tag tag) ReferredName?.Encode(writer); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); writer.WriteCharacterString(UniversalTagNumber.GeneralString, ReferredRealm); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); @@ -100,7 +114,9 @@ internal static void Decode(AsnReader reader, out T decoded) where T: KrbPaSvrReferralData, new() { if (reader == null) + { throw new ArgumentNullException(nameof(reader)); + } Decode(reader, Asn1Tag.Sequence, out decoded); } @@ -109,28 +125,28 @@ internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T deco where T: KrbPaSvrReferralData, new() { if (reader == null) + { throw new ArgumentNullException(nameof(reader)); + } decoded = new T(); + AsnReader sequenceReader = reader.ReadSequence(expectedTag); AsnReader explicitReader; - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - KrbPrincipalName tmpReferredName; - KrbPrincipalName.Decode(explicitReader, out tmpReferredName); + explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); + + KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpReferredName); decoded.ReferredName = tmpReferredName; - explicitReader.ThrowIfNotEmpty(); } - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); decoded.ReferredRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); + explicitReader.ThrowIfNotEmpty(); sequenceReader.ThrowIfNotEmpty(); } diff --git a/Kerberos.NET/Entities/Krb/KrbPrincipalName.generated.cs b/Kerberos.NET/Entities/Krb/KrbPrincipalName.generated.cs deleted file mode 100644 index 49e2d9fd..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPrincipalName.generated.cs +++ /dev/null @@ -1,173 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPrincipalName - { - /* - PrincipalName ::= SEQUENCE { - name-type [0] Int32, - name-string [1] SEQUENCE OF KerberosString - } - */ - - public PrincipalNameType Type { get; set; } - - public string[] Name { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(); - - for (int i = 0; i < Name.Length; i++) - { - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Name[i]); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPrincipalName Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPrincipalName Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPrincipalName Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPrincipalName decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPrincipalName Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPrincipalName decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPrincipalName, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPrincipalName, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out PrincipalNameType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - // Decode SEQUENCE OF for Name - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - string tmpItem; - - while (collectionReader.HasData) - { - tmpItem = collectionReader.ReadCharacterString(UniversalTagNumber.GeneralString); - tmpList.Add(tmpItem); - } - - decoded.Name = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPriv.generated.cs b/Kerberos.NET/Entities/Krb/KrbPriv.generated.cs deleted file mode 100644 index 5445d2c9..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPriv.generated.cs +++ /dev/null @@ -1,184 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPriv - { - /* - KRB-PRIV ::= [APPLICATION 21] SEQUENCE { - pvno [0] INTEGER (5), - msg-type [1] INTEGER (21), - // NOTE: there is no [2] tag - enc-part [3] EncryptedData // EncKrbPrivPart - - } - */ - - public int ProtocolVersionNumber { get; set; } - - public MessageType MessageType { get; set; } - - public KrbEncryptedData EncPart { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(ProtocolVersionNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)MessageType); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - EncPart?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 21); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbPriv DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbPriv decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbPriv DecodeApplication(AsnReader reader, out T decoded) - where T: KrbPriv, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbPriv Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPriv decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPriv Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPriv decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPriv, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPriv, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpProtocolVersionNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.ProtocolVersionNumber = tmpProtocolVersionNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out MessageType tmpMessageType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.MessageType = tmpMessageType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncPart); - decoded.EncPart = tmpEncPart; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbTgsRep.generated.cs b/Kerberos.NET/Entities/Krb/KrbTgsRep.generated.cs deleted file mode 100644 index 60a84375..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTgsRep.generated.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbTgsRep : KrbKdcRep - { - /* - TGS-REP ::= [APPLICATION 13] KDC-REQ - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 13); - - public override ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbTgsRep DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbTgsRep decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbTgsReq.generated.cs b/Kerberos.NET/Entities/Krb/KrbTgsReq.generated.cs deleted file mode 100644 index d20e6f43..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTgsReq.generated.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbTgsReq : KrbKdcReq - { - /* - TGS-REQ ::= [APPLICATION 12] KDC-REQ - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 12); - - public override ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbTgsReq DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbTgsReq decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbTicket.generated.cs b/Kerberos.NET/Entities/Krb/KrbTicket.generated.cs deleted file mode 100644 index c54998f4..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTicket.generated.cs +++ /dev/null @@ -1,188 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbTicket - { - /* - Ticket ::= [APPLICATION 1] SEQUENCE { - tkt-vno [0] INTEGER (5), - realm [1] Realm, - sname [2] PrincipalName, - enc-part [3] EncryptedData - } - */ - - public int TicketNumber { get; set; } - - public string Realm { get; set; } - - public KrbPrincipalName SName { get; set; } - - public KrbEncryptedData EncryptedPart { get; set; } - - // Encoding methods - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(TicketNumber); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Realm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - SName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - EncryptedPart?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, 1); - - public virtual ReadOnlyMemory EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static KrbTicket DecodeApplication(ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - KrbTicket decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static KrbTicket DecodeApplication(AsnReader reader, out T decoded) - where T: KrbTicket, new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - internal static KrbTicket Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbTicket decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbTicket Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbTicket decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbTicket, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbTicket, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpTicketNumber)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.TicketNumber = tmpTicketNumber; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.Realm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpSName); - decoded.SName = tmpSName; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncryptedPart); - decoded.EncryptedPart = tmpEncryptedPart; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbTransitedEncoding.generated.cs b/Kerberos.NET/Entities/Krb/KrbTransitedEncoding.generated.cs deleted file mode 100644 index 9254337e..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTransitedEncoding.generated.cs +++ /dev/null @@ -1,158 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbTransitedEncoding - { - /* - TransitedEncoding ::= SEQUENCE { - tr-type [0] Int32, - contents [1] OCTET STRING - } - */ - - public TransitedEncodingType Type { get; set; } - - public ReadOnlyMemory Contents { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(Contents.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbTransitedEncoding Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbTransitedEncoding Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbTransitedEncoding Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbTransitedEncoding decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbTransitedEncoding Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbTransitedEncoding decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbTransitedEncoding, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbTransitedEncoding, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out TransitedEncodingType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpContents)) - { - decoded.Contents = tmpContents; - } - else - { - decoded.Contents = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbAuthPack.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbAuthPack.generated.cs deleted file mode 100644 index 26c6a271..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbAuthPack.generated.cs +++ /dev/null @@ -1,242 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAuthPack - { - /* - AuthPack ::= SEQUENCE { - pkAuthenticator [0] PKAuthenticator, - clientPublicValue [1] SubjectPublicKeyInfo OPTIONAL, - - - Type SubjectPublicKeyInfo is defined in - - - [RFC3280]. - - - Specifies Diffie-Hellman domain parameters - - - and the client's public key value [IEEE1363]. - - - The DH public key value is encoded as a BIT - - - STRING according to [RFC3279]. - - - This field is present only if the client wishes - - - to use the Diffie-Hellman key agreement method. - supportedCMSTypes [2] SEQUENCE OF AlgorithmIdentifier - OPTIONAL, - - - Type AlgorithmIdentifier is defined in - - - [RFC3280]. - - - List of CMS algorithm [RFC3370] identifiers - - - that identify key transport algorithms, or - - - content encryption algorithms, or signature - - - algorithms supported by the client in order of - - - (decreasing) preference. - clientDHNonce [3] DHNonce OPTIONAL, - - - Present only if the client indicates that it - - - wishes to reuse DH keys or to allow the KDC to - - - do so. - ... - } - */ - - public KrbPKAuthenticator PKAuthenticator { get; set; } - - public KrbSubjectPublicKeyInfo ClientPublicValue { get; set; } - - public KrbAlgorithmIdentifier[] SupportedCMSTypes { get; set; } - - public ReadOnlyMemory? ClientDHNonce { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - PKAuthenticator?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(ClientPublicValue)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - ClientPublicValue?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(SupportedCMSTypes)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(); - - for (int i = 0; i < SupportedCMSTypes.Length; i++) - { - SupportedCMSTypes[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - - if (Asn1Extension.HasValue(ClientDHNonce)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteOctetString(ClientDHNonce.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbAuthPack Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbAuthPack Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbAuthPack Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbAuthPack decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbAuthPack Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbAuthPack decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbAuthPack, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbAuthPack, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbPKAuthenticator.Decode(explicitReader, out KrbPKAuthenticator tmpPKAuthenticator); - decoded.PKAuthenticator = tmpPKAuthenticator; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - KrbSubjectPublicKeyInfo.Decode(explicitReader, out KrbSubjectPublicKeyInfo tmpClientPublicValue); - decoded.ClientPublicValue = tmpClientPublicValue; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - // Decode SEQUENCE OF for SupportedCMSTypes - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbAlgorithmIdentifier tmpItem; - - while (collectionReader.HasData) - { - KrbAlgorithmIdentifier.Decode(collectionReader, out KrbAlgorithmIdentifier tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.SupportedCMSTypes = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpClientDHNonce)) - { - decoded.ClientDHNonce = tmpClientDHNonce; - } - else - { - decoded.ClientDHNonce = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.generated.cs deleted file mode 100644 index ccebcf8b..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.generated.cs +++ /dev/null @@ -1,174 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbDHReplyInfo - { - /* - DHRepInfo ::= SEQUENCE { - dhSignedData [0] IMPLICIT OCTET STRING, - - - Contains a CMS type ContentInfo encoded according - - - to [RFC3852]. - - - The contentType field of the type ContentInfo is - - - id-signedData (1.2.840.113549.1.7.2), and the - - - content field is a SignedData. - - - The eContentType field for the type SignedData is - - - id-pkinit-DHKeyData (1.3.6.1.5.2.3.2), and the - - - eContent field contains the DER encoding of the - - - type KDCDHKeyInfo. - - - KDCDHKeyInfo is defined below. - serverDHNonce [1] DHNonce OPTIONAL, - - - Present if and only if dhKeyExpiration is - - - present. - ... - } - */ - - public ReadOnlyMemory DHSignedData { get; set; } - - public ReadOnlyMemory? ServerDHNonce { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), DHSignedData.Span); - - if (Asn1Extension.HasValue(ServerDHNonce)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(ServerDHNonce.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbDHReplyInfo Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbDHReplyInfo Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbDHReplyInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbDHReplyInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbDHReplyInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbDHReplyInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbDHReplyInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbDHReplyInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out ReadOnlyMemory tmpDHSignedData)) - { - decoded.DHSignedData = tmpDHSignedData; - } - else - { - decoded.DHSignedData = sequenceReader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 0)); - } - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpServerDHNonce)) - { - decoded.ServerDHNonce = tmpServerDHNonce; - } - else - { - decoded.ServerDHNonce = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.generated.cs deleted file mode 100644 index c2801c12..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.generated.cs +++ /dev/null @@ -1,209 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbExternalPrincipalIdentifier - { - /* - ExternalPrincipalIdentifier ::= SEQUENCE { - subjectName [0] IMPLICIT OCTET STRING OPTIONAL, - - - Contains a PKIX type Name encoded according to - - - [RFC3280]. - - - Identifies the certificate subject by the - - - distinguished subject name. - - - REQUIRED when there is a distinguished subject - - - name present in the certificate. - issuerAndSerialNumber [1] IMPLICIT OCTET STRING OPTIONAL, - - - Contains a CMS type IssuerAndSerialNumber encoded - - - according to [RFC3852]. - - - Identifies a certificate of the subject. - - - REQUIRED for TD-INVALID-CERTIFICATES and - - - TD-TRUSTED-CERTIFIERS. - subjectKeyIdentifier [2] IMPLICIT OCTET STRING OPTIONAL, - - - Identifies the subject's public key by a key - - - identifier. When an X.509 certificate is - - - referenced, this key identifier matches the X.509 - - - subjectKeyIdentifier extension value. When other - - - certificate formats are referenced, the documents - - - that specify the certificate format and their use - - - with the CMS must include details on matching the - - - key identifier to the appropriate certificate - - - field. - - - RECOMMENDED for TD-TRUSTED-CERTIFIERS. - ... - } - */ - - public ReadOnlyMemory? SubjectName { get; set; } - - public ReadOnlyMemory? IssuerAndSerialNumber { get; set; } - - public ReadOnlyMemory? SubjectKeyIdentifier { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - - if (Asn1Extension.HasValue(SubjectName)) - { - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), SubjectName.Value.Span); - } - - if (Asn1Extension.HasValue(IssuerAndSerialNumber)) - { - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 1), IssuerAndSerialNumber.Value.Span); - } - - if (Asn1Extension.HasValue(SubjectKeyIdentifier)) - { - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 2), SubjectKeyIdentifier.Value.Span); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbExternalPrincipalIdentifier Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbExternalPrincipalIdentifier Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbExternalPrincipalIdentifier Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbExternalPrincipalIdentifier decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbExternalPrincipalIdentifier Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbExternalPrincipalIdentifier decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbExternalPrincipalIdentifier, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbExternalPrincipalIdentifier, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out ReadOnlyMemory tmpSubjectName)) - { - decoded.SubjectName = tmpSubjectName; - } - else - { - decoded.SubjectName = sequenceReader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 0)); - } - } - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 1), out ReadOnlyMemory tmpIssuerAndSerialNumber)) - { - decoded.IssuerAndSerialNumber = tmpIssuerAndSerialNumber; - } - else - { - decoded.IssuerAndSerialNumber = sequenceReader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - } - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 2), out ReadOnlyMemory tmpSubjectKeyIdentifier)) - { - decoded.SubjectKeyIdentifier = tmpSubjectKeyIdentifier; - } - else - { - decoded.SubjectKeyIdentifier = sequenceReader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.generated.cs deleted file mode 100644 index aab433ec..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.generated.cs +++ /dev/null @@ -1,184 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbKdcDHKeyInfo - { - /* - KDCDHKeyInfo ::= SEQUENCE { - subjectPublicKey [0] BIT STRING, - - - The KDC's DH public key. - - - The DH public key value is encoded as a BIT - - - STRING according to [RFC3279]. - nonce [1] INTEGER (0..4294967295), - - - Contains the nonce in the pkAuthenticator field - - - in the request if the DH keys are NOT reused, - - - 0 otherwise. - dhKeyExpiration [2] KerberosTime OPTIONAL, - - - Expiration time for KDC's key pair, - - - present if and only if the DH keys are reused. - - - If present, the KDC's DH public key MUST not be - - - used past the point of this expiration time. - - - If this field is omitted then the serverDHNonce - - - field MUST also be omitted. - ... - } - */ - - public ReadOnlyMemory SubjectPublicKey { get; set; } - - public System.Numerics.BigInteger Nonce { get; set; } - - public DateTimeOffset? DHKeyExpiration { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteBitString(SubjectPublicKey.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(Nonce); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (Asn1Extension.HasValue(DHKeyExpiration)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteGeneralizedTime(DHKeyExpiration.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbKdcDHKeyInfo Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbKdcDHKeyInfo Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbKdcDHKeyInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbKdcDHKeyInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbKdcDHKeyInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbKdcDHKeyInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbKdcDHKeyInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbKdcDHKeyInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpSubjectPublicKey)) - { - decoded.SubjectPublicKey = tmpSubjectPublicKey; - } - else - { - decoded.SubjectPublicKey = explicitReader.ReadBitString(out _); - } - - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.Nonce = explicitReader.ReadInteger(); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - decoded.DHKeyExpiration = explicitReader.ReadGeneralizedTime(); - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.generated.cs deleted file mode 100644 index ba88f777..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.generated.cs +++ /dev/null @@ -1,201 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPKAuthenticator - { - /* - PKAuthenticator ::= SEQUENCE { - cusec [0] INTEGER (0..999999), - ctime [1] KerberosTime, - - - cusec and ctime are used as in [RFC4120], for - - - replay prevention. - nonce [2] INTEGER (0..4294967295), - - - Chosen randomly; this nonce does not need to - - - match with the nonce in the KDC-REQ-BODY. - paChecksum [3] OCTET STRING OPTIONAL, - - - MUST be present. - - - Contains the SHA1 checksum, performed over - - - KDC-REQ-BODY. - ... - } - */ - - public int CuSec { get; set; } - - public DateTimeOffset CTime { get; set; } - - public int Nonce { get; set; } - - public ReadOnlyMemory? PaChecksum { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(CuSec); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteGeneralizedTime(CTime); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteInteger(Nonce); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (Asn1Extension.HasValue(PaChecksum)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteOctetString(PaChecksum.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPKAuthenticator Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPKAuthenticator Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPKAuthenticator Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPKAuthenticator decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPKAuthenticator Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPKAuthenticator decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPKAuthenticator, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPKAuthenticator, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpCuSec)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.CuSec = tmpCuSec; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.CTime = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (!explicitReader.TryReadInt32(out int tmpNonce)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Nonce = tmpNonce; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpPaChecksum)) - { - decoded.PaChecksum = tmpPaChecksum; - } - else - { - decoded.PaChecksum = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.generated.cs deleted file mode 100644 index 6e591977..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.generated.cs +++ /dev/null @@ -1,178 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaPkAsRep - { - /* - PA-PK-AS-REP ::= CHOICE { - dhInfo [0] DHRepInfo, - - - Selected when Diffie-Hellman key exchange is - - - used. - encKeyPack [1] IMPLICIT OCTET STRING, - - - Selected when public key encryption is used. - - - Contains a CMS type ContentInfo encoded - - - according to [RFC3852]. - - - The contentType field of the type ContentInfo is - - - id-envelopedData (1.2.840.113549.1.7.3). - - - The content field is an EnvelopedData. - - - The contentType field for the type EnvelopedData - - - is id-signedData (1.2.840.113549.1.7.2). - - - The eContentType field for the inner type - - - SignedData (when unencrypted) is - - - id-pkinit-rkeyData (1.3.6.1.5.2.3.3) and the - - - eContent field contains the DER encoding of the - - - type ReplyKeyPack. - - - ReplyKeyPack is defined below. - ... - } - */ - - public KrbDHReplyInfo DHInfo { get; set; } - - public ReadOnlyMemory? EncKeyPack { get; set; } - -#if DEBUG - static KrbPaPkAsRep() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 0), "DHInfo"); - ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 1), "EncKeyPack"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (Asn1Extension.HasValue(DHInfo)) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - DHInfo?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - wroteValue = true; - } - if (Asn1Extension.HasValue(EncKeyPack)) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(EncKeyPack.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - wroteValue = true; - } - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaPkAsRep Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaPkAsRep Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbPaPkAsRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaPkAsRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader explicitReader; - - if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbDHReplyInfo.Decode(explicitReader, out KrbDHReplyInfo tmpDHInfo); - decoded.DHInfo = tmpDHInfo; - explicitReader.ThrowIfNotEmpty(); - } - else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpEncKeyPack)) - { - decoded.EncKeyPack = tmpEncKeyPack; - } - else - { - decoded.EncKeyPack = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.generated.cs deleted file mode 100644 index c222a74a..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.generated.cs +++ /dev/null @@ -1,225 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaPkAsReq - { - /* - PA-PK-AS-REQ ::= SEQUENCE { - signedAuthPack [0] IMPLICIT OCTET STRING, - - - Contains a CMS type ContentInfo encoded - - - according to [RFC3852]. - - - The contentType field of the type ContentInfo - - - is id-signedData (1.2.840.113549.1.7.2), - - - and the content field is a SignedData. - - - The eContentType field for the type SignedData is - - - id-pkinit-authData (1.3.6.1.5.2.3.1), and the - - - eContent field contains the DER encoding of the - - - type AuthPack. - - - AuthPack is defined below. - trustedCertifiers [1] SEQUENCE OF - ExternalPrincipalIdentifier OPTIONAL, - - - Contains a list of CAs, trusted by the client, - - - that can be used to certify the KDC. - - - Each ExternalPrincipalIdentifier identifies a CA - - - or a CA certificate (thereby its public key). - - - The information contained in the - - - trustedCertifiers SHOULD be used by the KDC as - - - hints to guide its selection of an appropriate - - - certificate chain to return to the client. - kdcPkId [2] IMPLICIT OCTET STRING - OPTIONAL, - - - Contains a CMS type SignerIdentifier encoded - - - according to [RFC3852]. - - - Identifies, if present, a particular KDC - - - public key that the client already has. - ... - } - */ - - public ReadOnlyMemory SignedAuthPack { get; set; } - - public KrbExternalPrincipalIdentifier[] TrustedCertifiers { get; set; } - - public ReadOnlyMemory? KdcPkId { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 0), SignedAuthPack.Span); - - if (Asn1Extension.HasValue(TrustedCertifiers)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(); - - for (int i = 0; i < TrustedCertifiers.Length; i++) - { - TrustedCertifiers[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - - if (Asn1Extension.HasValue(KdcPkId)) - { - writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 2), KdcPkId.Value.Span); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaPkAsReq Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaPkAsReq Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaPkAsReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaPkAsReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaPkAsReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaPkAsReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaPkAsReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaPkAsReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 0), out ReadOnlyMemory tmpSignedAuthPack)) - { - decoded.SignedAuthPack = tmpSignedAuthPack; - } - else - { - decoded.SignedAuthPack = sequenceReader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 0)); - } - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - // Decode SEQUENCE OF for TrustedCertifiers - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbExternalPrincipalIdentifier tmpItem; - - while (collectionReader.HasData) - { - KrbExternalPrincipalIdentifier.Decode(collectionReader, out KrbExternalPrincipalIdentifier tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.TrustedCertifiers = tmpList.ToArray(); - } - explicitReader.ThrowIfNotEmpty(); - } - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - - if (sequenceReader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 2), out ReadOnlyMemory tmpKdcPkId)) - { - decoded.KdcPkId = tmpKdcPkId; - } - else - { - decoded.KdcPkId = sequenceReader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/SpNego/NegTokenInit.generated.cs b/Kerberos.NET/Entities/SpNego/NegTokenInit.generated.cs deleted file mode 100644 index 7321f20b..00000000 --- a/Kerberos.NET/Entities/SpNego/NegTokenInit.generated.cs +++ /dev/null @@ -1,236 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class NegTokenInit - { - /* - NegTokenInit ::= SEQUENCE { - mechTypes [0] MechTypeList, - reqFlags [1] ContextFlags OPTIONAL, - mechToken [2] OCTET STRING OPTIONAL, - mechListMIC [3] OCTET STRING OPTIONAL, - ... - } - */ - - public Oid[] MechTypes { get; set; } - - public ReadOnlyMemory? RequestFlags { get; set; } - - public ReadOnlyMemory? MechToken { get; set; } - - public ReadOnlyMemory? MechListMic { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(); - - for (int i = 0; i < MechTypes.Length; i++) - { - writer.WriteObjectIdentifier(MechTypes[i]); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(RequestFlags)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteBitString(RequestFlags.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(MechToken)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(MechToken.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(MechListMic)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteOctetString(MechListMic.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static NegTokenInit Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static NegTokenInit Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static NegTokenInit Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out NegTokenInit decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static NegTokenInit Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out NegTokenInit decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: NegTokenInit, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: NegTokenInit, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - // Decode SEQUENCE OF for MechTypes - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - Oid tmpItem; - - while (collectionReader.HasData) - { - tmpItem = collectionReader.ReadObjectIdentifier(); - tmpList.Add(tmpItem); - } - - decoded.MechTypes = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpRequestFlags)) - { - decoded.RequestFlags = tmpRequestFlags; - } - else - { - decoded.RequestFlags = explicitReader.ReadBitString(out _); - } - - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpMechToken)) - { - decoded.MechToken = tmpMechToken; - } - else - { - decoded.MechToken = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpMechListMic)) - { - decoded.MechListMic = tmpMechListMic; - } - else - { - decoded.MechListMic = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/SpNego/NegTokenResp.generated.cs b/Kerberos.NET/Entities/SpNego/NegTokenResp.generated.cs deleted file mode 100644 index 43b2b47f..00000000 --- a/Kerberos.NET/Entities/SpNego/NegTokenResp.generated.cs +++ /dev/null @@ -1,217 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class NegTokenResp - { - /* - NegTokenResp ::= SEQUENCE { - negState [0] ENUMERATED { - accept-completed (0), - accept-incomplete (1), - reject (2), - request-mic (3) - } OPTIONAL, - supportedMech [1] MechType OPTIONAL, - responseToken [2] OCTET STRING OPTIONAL, - mechListMIC [3] OCTET STRING OPTIONAL, - ... - } - */ - - public NegotiateState State { get; set; } - - public Oid SupportedMech { get; set; } - - public ReadOnlyMemory? ResponseToken { get; set; } - - public ReadOnlyMemory? MechListMic { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - - if (Asn1Extension.HasValue(State)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteEnumeratedValue(State); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - } - - if (Asn1Extension.HasValue(SupportedMech)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteObjectIdentifier(SupportedMech); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - - if (Asn1Extension.HasValue(ResponseToken)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(ResponseToken.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(MechListMic)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteOctetString(MechListMic.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static NegTokenResp Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static NegTokenResp Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static NegTokenResp Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out NegTokenResp decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static NegTokenResp Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out NegTokenResp decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: NegTokenResp, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: NegTokenResp, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - decoded.State = explicitReader.ReadEnumeratedValue(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - decoded.SupportedMech = explicitReader.ReadObjectIdentifier(); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpResponseToken)) - { - decoded.ResponseToken = tmpResponseToken; - } - else - { - decoded.ResponseToken = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpMechListMic)) - { - decoded.MechListMic = tmpMechListMic; - } - else - { - decoded.MechListMic = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/SpNego/NegotiationToken.generated.cs b/Kerberos.NET/Entities/SpNego/NegotiationToken.generated.cs deleted file mode 100644 index 2f50d1cd..00000000 --- a/Kerberos.NET/Entities/SpNego/NegotiationToken.generated.cs +++ /dev/null @@ -1,154 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class NegotiationToken - { - /* - NegotiationToken ::= CHOICE { - negTokenInit [0] NegTokenInit, - negTokenResp [1] NegTokenResp - } - */ - - public NegTokenInit InitialToken { get; set; } - - public NegTokenResp ResponseToken { get; set; } - -#if DEBUG - static NegotiationToken() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 0), "InitialToken"); - ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 1), "ResponseToken"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (Asn1Extension.HasValue(InitialToken)) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - InitialToken?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - wroteValue = true; - } - if (Asn1Extension.HasValue(ResponseToken)) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - ResponseToken?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - wroteValue = true; - } - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static NegotiationToken Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static NegotiationToken Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out NegotiationToken decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: NegotiationToken, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader explicitReader; - - if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - NegTokenInit.Decode(explicitReader, out NegTokenInit tmpInitialToken); - decoded.InitialToken = tmpInitialToken; - explicitReader.ThrowIfNotEmpty(); - } - else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - NegTokenResp.Decode(explicitReader, out NegTokenResp tmpResponseToken); - decoded.ResponseToken = tmpResponseToken; - explicitReader.ThrowIfNotEmpty(); - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Kerberos.NET.csproj b/Kerberos.NET/Kerberos.NET.csproj index 781b0f23..83d0999d 100644 --- a/Kerberos.NET/Kerberos.NET.csproj +++ b/Kerberos.NET/Kerberos.NET.csproj @@ -27,69 +27,46 @@ + + - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + + + + + + + + + + diff --git a/Kerberos.NET/Server/FastState.cs b/Kerberos.NET/Server/FastState.cs new file mode 100644 index 00000000..015bea35 --- /dev/null +++ b/Kerberos.NET/Server/FastState.cs @@ -0,0 +1,37 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.Server +{ + /// + /// Contains the state information of a FAST (RFC 6113) armored request + /// used between the pre-validation and validation phases. + /// + public class FastState : PaDataState + { + /// + /// The derived FAST armor key used to protect the tunnel. + /// + public KerberosKey ArmorKey { get; set; } + + /// + /// The inner FAST request extracted from the armored envelope. + /// + public KrbFastReq InnerRequest { get; set; } + + /// + /// A random key generated by the KDC to strengthen the reply key. + /// + public KerberosKey StrengthenKey { get; set; } + + /// + /// The decrypted armor AP-REQ containing the ticket and authenticator. + /// + public DecryptedKrbApReq DecryptedArmorApReq { get; set; } + } +} diff --git a/Kerberos.NET/Server/KdcAsReqMessageHandler.cs b/Kerberos.NET/Server/KdcAsReqMessageHandler.cs index e2e57ea5..616044f1 100644 --- a/Kerberos.NET/Server/KdcAsReqMessageHandler.cs +++ b/Kerberos.NET/Server/KdcAsReqMessageHandler.cs @@ -19,6 +19,7 @@ public class KdcAsReqMessageHandler : KdcMessageHandlerBase private static readonly PaDataType[] PreAuthAscendingPriority = new PaDataType[] { PaDataType.PA_PK_AS_REQ, + PaDataType.PA_ENCRYPTED_CHALLENGE, PaDataType.PA_ENC_TIMESTAMP, }; @@ -165,6 +166,9 @@ private ReadOnlyMemory GenerateAsRep(KrbAsReq asReq, PreAuthenticationCont // 5. encrypt against krbtgt // 6. done + // If FAST is in use, strengthen the reply key before generating the TGT + var fastState = GetFastState(context); + var rst = new ServiceTicketRequest { ClientRealmName = asReq.Body.Realm, @@ -217,7 +221,7 @@ private ReadOnlyMemory GenerateAsRep(KrbAsReq asReq, PreAuthenticationCont rst.SamAccountName = asReq.Body.CName.FullyQualifiedName; #pragma warning restore CS0618 // Type or member is obsolete } - + } if (rst.EncryptedPartKey == null) @@ -237,11 +241,41 @@ private ReadOnlyMemory GenerateAsRep(KrbAsReq asReq, PreAuthenticationCont rst.ClampLifetime(); + // FAST reply wrapping: strengthen the reply key before generating the TGT + // so the enc-part is encrypted with the strengthened key + var fastPaData = PaDataFastHandler.WrapFastResponse( + fastState, + context.PaData?.ToArray(), + null, + context + ); + + // Now EncryptedPartKey has been strengthened if FAST is active + rst.EncryptedPartKey = context.EncryptedPartKey; + var asRep = KrbAsRep.GenerateTgt(rst, this.RealmService); if (context.PaData != null) { - asRep.PaData = context.PaData.ToArray(); + var paDataList = context.PaData.ToList(); + + // Now generate the full FAST response with the ticket checksum + if (fastState?.ArmorKey != null) + { + var fullFastPaData = PaDataFastHandler.WrapFastResponse( + fastState, + context.PaData?.ToArray(), + asRep, + context + ); + + if (fullFastPaData != null) + { + paDataList.Add(fullFastPaData); + } + } + + asRep.PaData = paDataList.ToArray(); } return asRep.EncodeApplication(); @@ -259,6 +293,18 @@ private ReadOnlyMemory PreAuthFailed(PreAuthenticationContext context) err.StampServerTime(); + // If FAST is active, wrap the error in a FAST response + var fastState = GetFastState(context); + var fastError = PaDataFastHandler.WrapFastError(fastState, err); + + if (fastError != null) + { + err.EData = new KrbMethodData + { + MethodData = new[] { fastError } + }.Encode(); + } + return err.EncodeApplication(); } @@ -266,6 +312,20 @@ private ReadOnlyMemory RequirePreAuth(PreAuthenticationContext context) { this.logger.LogTrace("AS-REQ requires pre-auth for user {User}", context.Principal.PrincipalName); + var methodData = context.PaData.ToList(); + + // If FAST is active, include FAST-wrapped hints + var fastState = GetFastState(context); + + if (fastState?.ArmorKey != null) + { + // Add PA_ENCRYPTED_CHALLENGE hint when FAST is active + if (!methodData.Any(p => p.Type == PaDataType.PA_ENCRYPTED_CHALLENGE)) + { + methodData.Add(new KrbPaData { Type = PaDataType.PA_ENCRYPTED_CHALLENGE }); + } + } + var err = new KrbError { ErrorCode = KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED, @@ -274,7 +334,7 @@ private ReadOnlyMemory RequirePreAuth(PreAuthenticationContext context) SName = KrbPrincipalName.FromPrincipal(context.Principal), EData = new KrbMethodData { - MethodData = context.PaData.ToArray() + MethodData = methodData.ToArray() }.Encode() }; @@ -282,5 +342,16 @@ private ReadOnlyMemory RequirePreAuth(PreAuthenticationContext context) return err.EncodeApplication(); } + + private static FastState GetFastState(PreAuthenticationContext context) + { + if (context.PreAuthenticationState.TryGetValue(PaDataType.PA_FX_FAST, out PaDataState state) + && state is FastState fastState) + { + return fastState; + } + + return null; + } } } diff --git a/Kerberos.NET/Server/KdcServer.cs b/Kerberos.NET/Server/KdcServer.cs index ae25aae3..061410fa 100644 --- a/Kerberos.NET/Server/KdcServer.cs +++ b/Kerberos.NET/Server/KdcServer.cs @@ -42,6 +42,12 @@ public KdcServer(KdcServerOptions options) { this.RegisterPreAuthHandler(PaDataType.PA_PK_AS_REQ, (service) => new PaDataPkAsReqHandler(service)); } + + if (options.Configuration.KdcDefaults.RegisterDefaultFastHandler) + { + this.RegisterPreAuthHandler(PaDataType.PA_FX_FAST, (service) => new PaDataFastHandler(service)); + this.RegisterPreAuthHandler(PaDataType.PA_ENCRYPTED_CHALLENGE, (service) => new PaDataEncryptedChallengeHandler(service)); + } } if (options.Configuration.KdcDefaults.RegisterDefaultTgsReqHandler) diff --git a/Kerberos.NET/Server/PaDataEncryptedChallengeHandler.cs b/Kerberos.NET/Server/PaDataEncryptedChallengeHandler.cs new file mode 100644 index 00000000..e46d68a7 --- /dev/null +++ b/Kerberos.NET/Server/PaDataEncryptedChallengeHandler.cs @@ -0,0 +1,137 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Text; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using static Kerberos.NET.Entities.KerberosConstants; + +namespace Kerberos.NET.Server +{ + /// + /// Handles PA-ENCRYPTED-CHALLENGE pre-authentication within a FAST tunnel (RFC 6113 section 5.4.6). + /// The encrypted challenge replaces the traditional encrypted timestamp when FAST is in use. + /// + public class PaDataEncryptedChallengeHandler : KdcPreAuthenticationHandlerBase + { + public PaDataEncryptedChallengeHandler(IRealmService service) + : base(service) + { + } + + public override KrbPaData Validate(KrbKdcReq asReq, PreAuthenticationContext preauth) + { + if (asReq == null) + { + throw new ArgumentNullException(nameof(asReq)); + } + + if (preauth == null) + { + throw new ArgumentNullException(nameof(preauth)); + } + + if (preauth.PreAuthenticationSatisfied) + { + return null; + } + + // Retrieve the FAST state; encrypted challenge requires an active FAST tunnel + var fastState = preauth.GetState(PaDataType.PA_FX_FAST); + + if (fastState.ArmorKey == null) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + "PA-ENCRYPTED-CHALLENGE requires FAST armor" + ); + } + + // Find the encrypted challenge PA-Data + var paChallenge = Array.Find(asReq.PaData, p => p.Type == PaDataType.PA_ENCRYPTED_CHALLENGE); + + if (paChallenge == null) + { + // Return a hint that encrypted challenge is expected + return new KrbPaData + { + Type = PaDataType.PA_ENCRYPTED_CHALLENGE + }; + } + + var principal = preauth.Principal; + var clientKey = principal.RetrieveLongTermCredential(); + var etype = fastState.ArmorKey.EncryptionType; + + // Derive the challenge key: CF2(armor_key, client_long_term_key, "clientchallengearmor", "challengelongterm") + var challengeKeyBytes = KrbFx.Cf2( + fastState.ArmorKey.GetKey().ToArray(), + clientKey.GetKey().ToArray(), + Encoding.UTF8.GetBytes("clientchallengearmor"), + Encoding.UTF8.GetBytes("challengelongterm"), + etype + ); + + var challengeKey = new KerberosKey(key: challengeKeyBytes.ToArray(), etype: etype); + + // Decrypt the challenge using the client challenge key + var encData = KrbEncryptedData.Decode(paChallenge.Value); + + var timestamp = encData.Decrypt( + challengeKey, + KeyUsage.EncChallengeClient, + b => KrbPaEncTsEnc.Decode(b) + ); + + // Validate the timestamp is within allowed skew + var skew = this.Service.Settings.MaximumSkew; + var now = this.Service.Now(); + + if (!WithinSkew(now, timestamp.PaTimestamp, timestamp.PaUSec ?? 0, skew)) + { + throw new KerberosValidationException( + $"Encrypted challenge timestamp is outside allowed skew. Timestamp: {timestamp.PaTimestamp}; Now: {now}; Skew: {skew}" + ); + } + + // Build the KDC's encrypted challenge response + Now(out DateTimeOffset kdcTime, out int kdcUsec); + + var kdcChallenge = new KrbPaEncTsEnc + { + PaTimestamp = kdcTime, + PaUSec = kdcUsec + }; + + // Derive KDC challenge key (same derivation, but encrypted with KDC usage) + var kdcChallengeKeyBytes = KrbFx.Cf2( + fastState.ArmorKey.GetKey().ToArray(), + clientKey.GetKey().ToArray(), + Encoding.UTF8.GetBytes("kdcchallengearmor"), + Encoding.UTF8.GetBytes("challengelongterm"), + etype + ); + + var kdcChallengeKey = new KerberosKey(key: kdcChallengeKeyBytes.ToArray(), etype: etype); + + var encKdcChallenge = KrbEncryptedData.Encrypt( + kdcChallenge.Encode(), + kdcChallengeKey, + KeyUsage.EncChallengeKdc + ); + + // Mark pre-auth satisfied with the client's long-term key + preauth.EncryptedPartKey = clientKey; + preauth.ClientAuthority = PaDataType.PA_ENCRYPTED_CHALLENGE; + + return new KrbPaData + { + Type = PaDataType.PA_ENCRYPTED_CHALLENGE, + Value = encKdcChallenge.Encode() + }; + } + } +} diff --git a/Kerberos.NET/Server/PaDataFastHandler.cs b/Kerberos.NET/Server/PaDataFastHandler.cs new file mode 100644 index 00000000..59658f15 --- /dev/null +++ b/Kerberos.NET/Server/PaDataFastHandler.cs @@ -0,0 +1,313 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using static Kerberos.NET.Entities.KerberosConstants; + +namespace Kerberos.NET.Server +{ + /// + /// Handles FAST (RFC 6113) armored pre-authentication for the KDC. + /// This handler unwraps FAST-armored requests during PreValidate and + /// wraps responses during PostValidate. + /// + public class PaDataFastHandler : KdcPreAuthenticationHandlerBase + { + public PaDataFastHandler(IRealmService service) + : base(service) + { + } + + /// + /// Unwraps the FAST armor from the request, derives the armor key, + /// verifies the request checksum, and decrypts the inner FAST request. + /// + public override void PreValidate(PreAuthenticationContext preauth) + { + if (preauth == null) + { + throw new ArgumentNullException(nameof(preauth)); + } + + var asReq = (KrbKdcReq)preauth.Message; + + if (asReq.PaData == null) + { + return; + } + + var paFast = asReq.PaData.FirstOrDefault(p => p.Type == PaDataType.PA_FX_FAST); + + if (paFast == null) + { + return; + } + + var state = preauth.GetState(PaDataType.PA_FX_FAST); + + // Decode PA-FX-FAST-REQUEST -> KrbFastArmoredReq + var fastRequest = KrbPaFxFastRequest.Decode(paFast.Value); + var armoredReq = fastRequest.ArmoredData; + + if (armoredReq == null) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + "FAST armored request is missing armored data" + ); + } + + // The armor field MUST be present in AS-REQ + if (armoredReq.Armor == null) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + "FAST armor is required for AS-REQ" + ); + } + + if (armoredReq.Armor.Type != KrbArmorType.FX_FAST_ARMOR_AP_REQUEST) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + $"Unsupported FAST armor type: {armoredReq.Armor.Type}" + ); + } + + // Decrypt the armor AP-REQ to obtain the armor ticket session key + var armorApReq = KrbApReq.DecodeApplication(armoredReq.Armor.Value.Value); + var decryptedArmorApReq = DecryptArmorApReq(armorApReq); + + state.DecryptedArmorApReq = decryptedArmorApReq; + + // Derive the armor key per RFC 6113 section 5.4.2: + // armor_key = CF2(subkey, ticket_session_key, "subkeyarmor", "ticketarmor") + var subkey = decryptedArmorApReq.Authenticator.Subkey + ?? throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + "FAST armor AP-REQ must contain an authenticator subkey" + ); + + var ticketSessionKey = decryptedArmorApReq.Ticket.Key; + var etype = subkey.EType; + + var armorKeyBytes = KrbFx.Cf2( + subkey.KeyValue.ToArray(), + ticketSessionKey.KeyValue.ToArray(), + Encoding.UTF8.GetBytes("subkeyarmor"), + Encoding.UTF8.GetBytes("ticketarmor"), + etype + ); + + var armorKey = new KerberosKey(key: armorKeyBytes.ToArray(), etype: etype); + state.ArmorKey = armorKey; + + // Verify the request checksum over the outer KDC-REQ-BODY + var outerBodyEncoded = asReq.Body.Encode(); + + var checksumType = CryptoService.ConvertType(armorKey.EncryptionType); + var checksumValidator = CryptoService.CreateChecksum( + checksumType, + armoredReq.RequestChecksum.Checksum, + outerBodyEncoded + ); + + checksumValidator.Usage = KeyUsage.FastReqChecksum; + checksumValidator.Validate(armorKey); + + // Decrypt the inner FAST request + var innerRequest = armoredReq.EncryptedFastRequest.Decrypt( + armorKey, + KeyUsage.FastEnc, + b => KrbFastReq.Decode(b) + ); + + state.InnerRequest = innerRequest; + } + + /// + /// Builds the FAST response, strengthens the reply key, and wraps + /// the response PA-Data in a FAST armored reply. + /// + public override void PostValidate(IKerberosPrincipal principal, List preAuthRequirements) + { + // PostValidate doesn't have direct access to PreAuthenticationContext, + // so FAST response wrapping is handled in KdcAsReqMessageHandler instead. + // This method is intentionally left empty. + } + + private DecryptedKrbApReq DecryptArmorApReq(KrbApReq armorApReq) + { + // The armor AP-REQ uses a TGT. We need to find the krbtgt key to decrypt it. + var krbtgtIdentity = this.Service.Principals.Find(armorApReq.Ticket.SName, armorApReq.Ticket.Realm); + + if (krbtgtIdentity == null) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_S_PRINCIPAL_UNKNOWN, + "Cannot find the service principal for the FAST armor ticket" + ); + } + + var krbtgtKey = krbtgtIdentity.RetrieveLongTermCredential(); + + if (krbtgtKey == null) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_ETYPE_NOSUPP, + "Cannot retrieve the key for the FAST armor ticket service" + ); + } + + var decrypted = new DecryptedKrbApReq(armorApReq); + + decrypted.Decrypt(krbtgtKey); + + decrypted.Validate(ValidationActions.All & ~ValidationActions.Replay & ~ValidationActions.ChannelBinding); + + return decrypted; + } + + /// + /// Creates the FAST response wrapping for an AS-REP or error. + /// Called from the message handler after the reply is generated. + /// + public static KrbPaData WrapFastResponse( + FastState state, + KrbPaData[] innerPaData, + KrbKdcRep rep, + PreAuthenticationContext preauth) + { + if (state?.ArmorKey == null) + { + return null; + } + + var etype = state.ArmorKey.EncryptionType; + + // Generate a random strengthen key + var strengthenKeyData = KrbEncryptionKey.Generate(etype); + state.StrengthenKey = strengthenKeyData.AsKey(); + + // Strengthen the reply key: CF2(reply_key, strengthen_key, "strengthenkey", "replykey") + if (preauth.EncryptedPartKey != null) + { + var strengthenedKeyBytes = KrbFx.Cf2( + preauth.EncryptedPartKey.GetKey().ToArray(), + state.StrengthenKey.GetKey().ToArray(), + Encoding.UTF8.GetBytes("strengthenkey"), + Encoding.UTF8.GetBytes("replykey"), + etype + ); + + preauth.EncryptedPartKey = new KerberosKey(key: strengthenedKeyBytes.ToArray(), etype: etype); + } + + // Build KrbFastFinished with ticket checksum + KrbFastFinished finished = null; + + if (rep != null) + { + Now(out DateTimeOffset timestamp, out int usec); + + finished = new KrbFastFinished + { + Timestamp = timestamp, + USec = usec, + CRealm = rep.CRealm, + CName = rep.CName, + TicketChecksum = KrbChecksum.Create( + rep.Ticket.EncodeApplication(), + state.ArmorKey, + KeyUsage.FastFinished + ) + }; + } + + // Build the FAST response + var fastResponse = new KrbFastResponse + { + PaData = innerPaData ?? Array.Empty(), + StrengthenKey = strengthenKeyData, + Finished = finished, + Nonce = state.InnerRequest?.ReqBody?.Nonce ?? 0 + }; + + // Encrypt the FAST response with the armor key + var encFastRep = KrbEncryptedData.Encrypt( + fastResponse.Encode(), + state.ArmorKey, + KeyUsage.FastRep + ); + + var armoredRep = new KrbFastArmoredRep + { + EncFastRep = encFastRep + }; + + var fastReply = new KrbPaFxFastReply + { + ArmoredData = armoredRep + }; + + return new KrbPaData + { + Type = PaDataType.PA_FX_FAST, + Value = fastReply.Encode() + }; + } + + /// + /// Wraps an error response in PA_FX_ERROR when FAST is active. + /// + public static KrbPaData WrapFastError(FastState state, KrbError error) + { + if (state?.ArmorKey == null || error == null) + { + return null; + } + + var fastResponse = new KrbFastResponse + { + PaData = new[] + { + new KrbPaData + { + Type = PaDataType.PA_FX_ERROR, + Value = error.EncodeApplication() + } + }, + Nonce = state.InnerRequest?.ReqBody?.Nonce ?? 0 + }; + + var encFastRep = KrbEncryptedData.Encrypt( + fastResponse.Encode(), + state.ArmorKey, + KeyUsage.FastRep + ); + + var armoredRep = new KrbFastArmoredRep + { + EncFastRep = encFastRep + }; + + var fastReply = new KrbPaFxFastReply + { + ArmoredData = armoredRep + }; + + return new KrbPaData + { + Type = PaDataType.PA_FX_FAST, + Value = fastReply.Encode() + }; + } + } +} diff --git a/Kerberos.NET/kerberos.asn b/Kerberos.NET/kerberos.asn index 87586bc7..a5d4226c 100644 --- a/Kerberos.NET/kerberos.asn +++ b/Kerberos.NET/kerberos.asn @@ -1,19 +1,3 @@ -GSS-API DEFINITIONS ::= - -BEGIN - -MechType ::= OBJECT IDENTIFIER --- representing Kerberos V5 mechanism - -GSSAPI-Token ::= [APPLICATION 0] IMPLICIT SEQUENCE { - thisMech MechType, - innerToken ANY DEFINED BY thisMech - -- contents mechanism-specific - -- ASN.1 structure not required -} - -END - SPNEGOASNOneSpec { iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanism(5) snego (2) modules(4) spec2(2) @@ -285,7 +269,8 @@ EncKDCRepPart ::= SEQUENCE { renew-till [8] KerberosTime OPTIONAL, srealm [9] Realm, sname [10] PrincipalName, - caddr [11] HostAddresses OPTIONAL + caddr [11] HostAddresses OPTIONAL, + encrypted-pa-data [12] METHOD-DATA OPTIONAL } LastReq ::= SEQUENCE OF SEQUENCE { diff --git a/Kerberos.NET/kerberos.asn.json b/Kerberos.NET/kerberos.asn.json new file mode 100644 index 00000000..86de6c3c --- /dev/null +++ b/Kerberos.NET/kerberos.asn.json @@ -0,0 +1,456 @@ +{ + "Types": { + "PrincipalName": { + "CSharpName": "KrbPrincipalName", + "Fields": { + "name-type": { "CSharpName": "Type", "EnumType": "PrincipalNameType", "TreatAsEnum": true }, + "name-string": { "CSharpName": "Name", "BackingType": "string[]" } + } + }, + "HostAddress": { + "CSharpName": "KrbHostAddress", + "Fields": { + "addr-type": { "CSharpName": "AddressType", "EnumType": "AddressType", "TreatAsEnum": true }, + "address": { "CSharpName": "Address" } + } + }, + "AuthorizationData": { + "CSharpName": "KrbAuthorizationData", + "Fields": { + "ad-type": { "CSharpName": "Type", "EnumType": "AuthorizationDataType", "TreatAsEnum": true }, + "ad-data": { "CSharpName": "Data" } + } + }, + "PA-DATA": { + "CSharpName": "KrbPaData", + "Fields": { + "padata-type": { "CSharpName": "Type", "EnumType": "PaDataType", "TreatAsEnum": true }, + "padata-value": { "CSharpName": "Value" } + } + }, + "EncryptedData": { + "CSharpName": "KrbEncryptedData", + "Fields": { + "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, + "kvno": { "CSharpName": "KeyVersionNumber" }, + "cipher": { "CSharpName": "Cipher" } + } + }, + "EncryptionKey": { + "CSharpName": "KrbEncryptionKey", + "Fields": { + "keytype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, + "keyvalue": { "CSharpName": "KeyValue" } + } + }, + "Checksum": { + "CSharpName": "KrbChecksum", + "Fields": { + "cksumtype": { "CSharpName": "Type", "EnumType": "ChecksumType", "TreatAsEnum": true }, + "checksum": { "CSharpName": "Checksum" } + } + }, + "TransitedEncoding": { + "CSharpName": "KrbTransitedEncoding", + "Fields": { + "tr-type": { "CSharpName": "Type", "EnumType": "TransitedEncodingType", "TreatAsEnum": true }, + "contents": { "CSharpName": "Contents" } + } + }, + "Ticket": { + "CSharpName": "KrbTicket", + "Fields": { + "tkt-vno": { "CSharpName": "TicketNumber" }, + "realm": { "CSharpName": "Realm" }, + "sname": { "CSharpName": "SName" }, + "enc-part": { "CSharpName": "EncryptedPart" } + } + }, + "EncTicketPart": { + "CSharpName": "KrbEncTicketPart", + "Fields": { + "flags": { "CSharpName": "Flags", "EnumType": "TicketFlags", "TreatAsEnum": true }, + "key": { "CSharpName": "Key" }, + "crealm": { "CSharpName": "CRealm" }, + "cname": { "CSharpName": "CName" }, + "transited": { "CSharpName": "Transited" }, + "authtime": { "CSharpName": "AuthTime" }, + "starttime": { "CSharpName": "StartTime" }, + "endtime": { "CSharpName": "EndTime" }, + "renew-till": { "CSharpName": "RenewTill" }, + "caddr": { "CSharpName": "CAddr" }, + "authorization-data": { "CSharpName": "AuthorizationData" } + } + }, + "KDC-REQ": { + "CSharpName": "KrbKdcReq", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "padata": { "CSharpName": "PaData" }, + "req-body": { "CSharpName": "Body" } + } + }, + "KDC-REQ-BODY": { + "CSharpName": "KrbKdcReqBody", + "Fields": { + "kdc-options": { "CSharpName": "KdcOptions", "EnumType": "KdcOptions", "TreatAsEnum": true }, + "cname": { "CSharpName": "CName" }, + "realm": { "CSharpName": "Realm" }, + "sname": { "CSharpName": "SName" }, + "from": { "CSharpName": "From" }, + "till": { "CSharpName": "Till" }, + "rtime": { "CSharpName": "RTime" }, + "nonce": { "CSharpName": "Nonce" }, + "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, + "addresses": { "CSharpName": "Addresses" }, + "enc-authorization-data": { "CSharpName": "EncAuthorizationData" }, + "additional-tickets": { "CSharpName": "AdditionalTickets" } + } + }, + "AS-REQ": { + "CSharpName": "KrbAsReq", + "InheritsFrom": "KrbKdcReq" + }, + "TGS-REQ": { + "CSharpName": "KrbTgsReq", + "InheritsFrom": "KrbKdcReq" + }, + "KDC-REP": { + "CSharpName": "KrbKdcRep", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "padata": { "CSharpName": "PaData" }, + "crealm": { "CSharpName": "CRealm" }, + "cname": { "CSharpName": "CName" }, + "ticket": { "CSharpName": "Ticket" }, + "enc-part": { "CSharpName": "EncPart" } + } + }, + "AS-REP": { + "CSharpName": "KrbAsRep", + "InheritsFrom": "KrbKdcRep" + }, + "TGS-REP": { + "CSharpName": "KrbTgsRep", + "InheritsFrom": "KrbKdcRep" + }, + "EncASRepPart": { + "CSharpName": "KrbEncAsRepPart", + "InheritsFrom": "KrbEncKdcRepPart" + }, + "EncTGSRepPart": { + "CSharpName": "KrbEncTgsRepPart", + "InheritsFrom": "KrbEncKdcRepPart" + }, + "EncKDCRepPart": { + "CSharpName": "KrbEncKdcRepPart", + "Fields": { + "key": { "CSharpName": "Key" }, + "last-req": { "CSharpName": "LastReq" }, + "nonce": { "CSharpName": "Nonce" }, + "key-expiration": { "CSharpName": "KeyExpiration" }, + "flags": { "CSharpName": "Flags", "EnumType": "TicketFlags", "TreatAsEnum": true }, + "authtime": { "CSharpName": "AuthTime" }, + "starttime": { "CSharpName": "StartTime" }, + "endtime": { "CSharpName": "EndTime" }, + "renew-till": { "CSharpName": "RenewTill" }, + "srealm": { "CSharpName": "Realm" }, + "sname": { "CSharpName": "SName" }, + "caddr": { "CSharpName": "CAddr" }, + "encrypted-pa-data": { "CSharpName": "EncryptedPaData", "BackingType": "KrbMethodData" } + } + }, + "LastReq": { + "CSharpName": "KrbLastReq", + "Fields": { + "lr-type": { "CSharpName": "Type" }, + "lr-value": { "CSharpName": "Value" } + } + }, + "AP-REQ": { + "CSharpName": "KrbApReq", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "ap-options": { "CSharpName": "ApOptions", "EnumType": "ApOptions", "TreatAsEnum": true }, + "ticket": { "CSharpName": "Ticket" }, + "authenticator": { "CSharpName": "Authenticator" } + } + }, + "Authenticator": { + "CSharpName": "KrbAuthenticator", + "Fields": { + "authenticator-vno": { "CSharpName": "AuthenticatorVersionNumber" }, + "crealm": { "CSharpName": "CRealm" }, + "cname": { "CSharpName": "CName" }, + "cksum": { "CSharpName": "Checksum" }, + "cusec": { "CSharpName": "CuSec" }, + "ctime": { "CSharpName": "CTime" }, + "subkey": { "CSharpName": "Subkey" }, + "seq-number": { "CSharpName": "SequenceNumber" }, + "authorization-data": { "CSharpName": "AuthorizationData" } + } + }, + "AP-REP": { + "CSharpName": "KrbApRep", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "enc-part": { "CSharpName": "EncryptedPart" } + } + }, + "EncAPRepPart": { + "CSharpName": "KrbEncApRepPart", + "Fields": { + "ctime": { "CSharpName": "CTime" }, + "cusec": { "CSharpName": "CuSec" }, + "subkey": { "CSharpName": "SubSessionKey" }, + "seq-number": { "CSharpName": "SequenceNumber" } + } + }, + "KRB-SAFE": { + "CSharpName": "KrbSafe", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "safe-body": { "CSharpName": "SafeBody" }, + "cksum": { "CSharpName": "Checksum" } + } + }, + "KRB-SAFE-BODY": { + "CSharpName": "KrbSafeBody", + "Fields": { + "user-data": { "CSharpName": "UserData" }, + "timestamp": { "CSharpName": "Timestamp" }, + "usec": { "CSharpName": "Usec" }, + "seq-number": { "CSharpName": "SeqNumber" }, + "s-address": { "CSharpName": "SAddress" }, + "r-address": { "CSharpName": "RAddress" } + } + }, + "KRB-PRIV": { + "CSharpName": "KrbPriv", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "enc-part": { "CSharpName": "EncPart" } + } + }, + "EncKrbPrivPart": { + "CSharpName": "KrbEncKrbPrivPart", + "Fields": { + "user-data": { "CSharpName": "UserData" }, + "timestamp": { "CSharpName": "Timestamp" }, + "usec": { "CSharpName": "Usec" }, + "seq-number": { "CSharpName": "SeqNumber" }, + "s-address": { "CSharpName": "SAddress" }, + "r-address": { "CSharpName": "RAddress" } + } + }, + "KRB-CRED": { + "CSharpName": "KrbCred", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "tickets": { "CSharpName": "Tickets" }, + "enc-part": { "CSharpName": "EncryptedPart" } + } + }, + "EncKrbCredPart": { + "CSharpName": "KrbEncKrbCredPart", + "Fields": { + "ticket-info": { "CSharpName": "TicketInfo" }, + "nonce": { "CSharpName": "Nonce" }, + "timestamp": { "CSharpName": "Timestamp" }, + "usec": { "CSharpName": "USec" }, + "s-address": { "CSharpName": "SAddress" }, + "r-address": { "CSharpName": "RAddress" } + } + }, + "KrbCredInfo": { + "CSharpName": "KrbCredInfo", + "Fields": { + "key": { "CSharpName": "Key" }, + "prealm": { "CSharpName": "Realm" }, + "pname": { "CSharpName": "PName" }, + "flags": { "CSharpName": "Flags", "EnumType": "TicketFlags", "TreatAsEnum": true }, + "authtime": { "CSharpName": "AuthTime" }, + "starttime": { "CSharpName": "StartTime" }, + "endtime": { "CSharpName": "EndTime" }, + "renew-till": { "CSharpName": "RenewTill" }, + "srealm": { "CSharpName": "SRealm" }, + "sname": { "CSharpName": "SName" }, + "caddr": { "CSharpName": "AuthorizationData", "BackingType": "KrbAuthorizationData[]" } + } + }, + "KRB-ERROR": { + "CSharpName": "KrbError", + "Fields": { + "pvno": { "CSharpName": "ProtocolVersionNumber" }, + "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, + "ctime": { "CSharpName": "CTime" }, + "cusec": { "CSharpName": "Cusec" }, + "stime": { "CSharpName": "STime" }, + "susec": { "CSharpName": "Susc" }, + "error-code": { "CSharpName": "ErrorCode", "EnumType": "KerberosErrorCode", "TreatAsEnum": true }, + "crealm": { "CSharpName": "CRealm" }, + "cname": { "CSharpName": "CName" }, + "realm": { "CSharpName": "Realm" }, + "sname": { "CSharpName": "SName" }, + "e-text": { "CSharpName": "EText" }, + "e-data": { "CSharpName": "EData" } + } + }, + "TYPED-DATA": { + "CSharpName": "KrbTypedData", + "Fields": { + "data-type": { "CSharpName": "Type" }, + "data-value": { "CSharpName": "Value" } + } + }, + "PA-ENC-TS-ENC": { + "CSharpName": "KrbPaEncTsEnc", + "Fields": { + "patimestamp": { "CSharpName": "PaTimestamp" }, + "pausec": { "CSharpName": "PaUSec" } + } + }, + "ETYPE-INFO-ENTRY": { + "CSharpName": "KrbETypeInfoEntry", + "Fields": { + "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, + "salt": { "CSharpName": "Salt" } + } + }, + "ETYPE-INFO2-ENTRY": { + "CSharpName": "KrbETypeInfo2Entry", + "Fields": { + "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, + "salt": { "CSharpName": "Salt" }, + "s2kparams": { "CSharpName": "S2kParams" } + } + }, + "AD-KDCIssued": { + "CSharpName": "KrbAdKdcIssued", + "Fields": { + "ad-checksum": { "CSharpName": "Checksum" }, + "i-realm": { "CSharpName": "IRealm" }, + "i-sname": { "CSharpName": "ISName" }, + "elements": { "CSharpName": "Elements" } + } + }, + "AD-AND-OR": { + "CSharpName": "KrbAdAndOr", + "Fields": { + "condition-count": { "CSharpName": "ConditionCount" }, + "elements": { "CSharpName": "Elements" } + } + }, + "NegotiationToken": { + "CSharpName": "NegotiationToken", + "Namespace": "Kerberos.NET.Entities", + "Fields": { + "negTokenInit": { "CSharpName": "InitialToken" }, + "negTokenResp": { "CSharpName": "ResponseToken" } + } + }, + "NegTokenInit": { + "CSharpName": "NegTokenInit", + "Namespace": "Kerberos.NET.Entities", + "Fields": { + "mechTypes": { "CSharpName": "MechTypes", "BackingType": "Oid[]" }, + "reqFlags": { "CSharpName": "RequestFlags" }, + "mechToken": { "CSharpName": "MechToken" }, + "mechListMIC": { "CSharpName": "MechListMic" } + } + }, + "NegTokenResp": { + "CSharpName": "NegTokenResp", + "Namespace": "Kerberos.NET.Entities", + "Fields": { + "negState": { "CSharpName": "State", "EnumType": "NegotiateState", "TreatAsEnum": true }, + "supportedMech": { "CSharpName": "SupportedMech", "BackingType": "Oid" }, + "responseToken": { "CSharpName": "ResponseToken" }, + "mechListMIC": { "CSharpName": "MechListMic" } + } + }, + "PA-PK-AS-REQ": { + "CSharpName": "KrbPaPkAsReq", + "Fields": { + "signedAuthPack": { "CSharpName": "SignedAuthPack" }, + "trustedCertifiers": { "CSharpName": "TrustedCertifiers" }, + "kdcPkId": { "CSharpName": "KdcPkId" } + } + }, + "ExternalPrincipalIdentifier": { + "CSharpName": "KrbExternalPrincipalIdentifier", + "Fields": { + "subjectName": { "CSharpName": "SubjectName" }, + "issuerAndSerialNumber": { "CSharpName": "IssuerAndSerialNumber" }, + "subjectKeyIdentifier": { "CSharpName": "SubjectKeyIdentifier" } + } + }, + "AuthPack": { + "CSharpName": "KrbAuthPack", + "Fields": { + "pkAuthenticator": { "CSharpName": "PKAuthenticator" }, + "clientPublicValue": { "CSharpName": "ClientPublicValue" }, + "supportedCMSTypes": { "CSharpName": "SupportedCMSTypes" }, + "clientDHNonce": { "CSharpName": "ClientDHNonce" } + } + }, + "PKAuthenticator": { + "CSharpName": "KrbPKAuthenticator", + "Fields": { + "cusec": { "CSharpName": "CuSec" }, + "ctime": { "CSharpName": "CTime" }, + "nonce": { "CSharpName": "Nonce" }, + "paChecksum": { "CSharpName": "PaChecksum" } + } + }, + "KRB5PrincipalName": { + "CSharpName": "KrbPrincipal5Name", + "Fields": { + "realm": { "CSharpName": "Realm" }, + "principalName": { "CSharpName": "PrincipalName" } + } + }, + "PA-PK-AS-REP": { + "CSharpName": "KrbPaPkAsRep", + "Fields": { + "dhInfo": { "CSharpName": "DHInfo" }, + "encKeyPack": { "CSharpName": "EncKeyPack" } + } + }, + "DHRepInfo": { + "CSharpName": "KrbDHReplyInfo", + "Fields": { + "dhSignedData": { "CSharpName": "DHSignedData" }, + "serverDHNonce": { "CSharpName": "ServerDHNonce" } + } + }, + "KDCDHKeyInfo": { + "CSharpName": "KrbKdcDHKeyInfo", + "Fields": { + "subjectPublicKey": { "CSharpName": "SubjectPublicKey" }, + "nonce": { "CSharpName": "Nonce" }, + "dhKeyExpiration": { "CSharpName": "DHKeyExpiration" } + } + }, + "ReplyKeyPack": { + "CSharpName": "KrbReplyKeyPack", + "Fields": { + "replyKey": { "CSharpName": "ReplyKey" }, + "asChecksum": { "CSharpName": "AsChecksum" } + } + }, + "SubjectPublicKeyInfo": { + "CSharpName": "KrbSubjectPublicKeyInfo" + }, + "AlgorithmIdentifier": { + "CSharpName": "KrbAlgorithmIdentifier" + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Kdc/FastTests.cs b/Tests/Tests.Kerberos.NET/Kdc/FastTests.cs new file mode 100644 index 00000000..2402a291 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Kdc/FastTests.cs @@ -0,0 +1,331 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Kerberos.NET; +using Kerberos.NET.Client; +using Kerberos.NET.Configuration; +using Kerberos.NET.Credentials; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Server; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class FastTests : BaseTest + { + private const string Realm = "CORP2.IDENTITYINTERVENTION.COM"; + private const string Upn = "fake@" + Realm; + private const string Password = "P@ssw0rd!"; + + private static KerberosPasswordCredential CreateCredential() + { + return new KerberosPasswordCredential(Upn, Password, Realm) + { + Salts = new[] + { + new KeyValuePair( + EncryptionType.AES256_CTS_HMAC_SHA1_96, + "CORP.IDENTITYINTERVENTION.COMfake@CORP2.IDENTITYINTERVENTION.COM" + ) + }, + Configuration = Krb5Config.Default() + }; + } + + private static KdcServerOptions CreateServerOptions() + { + return new KdcServerOptions + { + DefaultRealm = Realm, + IsDebug = true, + RealmLocator = realm => new FakeRealmService(realm) + }; + } + + /// + /// First, get a TGT without FAST (to use as armor ticket), + /// then use that TGT as FAST armor for a second AS-REQ. + /// + private (KrbAsRep armorTgt, KrbEncKdcRepPart armorDecrypted) GetArmorTgt() + { + var cred = CreateCredential(); + + var asReq = KrbAsReq.CreateAsReq(cred, AuthenticationOptions.AllAuthentication); + + var handler = new KdcAsReqMessageHandler(asReq.EncodeApplication(), CreateServerOptions()); + + handler.PreAuthHandlers[PaDataType.PA_ENC_TIMESTAMP] = service => new PaDataTimestampHandler(service); + + var results = handler.Execute(); + + var asRep = KrbAsRep.DecodeApplication(results); + + var decrypted = cred.DecryptKdcRep( + asRep, + KeyUsage.EncAsRepPart, + d => KrbEncAsRepPart.DecodeApplication(d) + ); + + return (asRep, decrypted); + } + + [TestMethod] + public void FastArmorContext_WrapUnwrap_RoundTrip() + { + // Get an armor TGT + var (armorTgt, armorDecrypted) = GetArmorTgt(); + + // Create armor context + var armorContext = new FastArmorContext(armorTgt, armorDecrypted); + + // Create a simple AS-REQ to wrap + var cred = CreateCredential(); + var asReq = KrbAsReq.CreateAsReq(cred, AuthenticationOptions.AllAuthentication); + + // Wrap the request + var wrappedReq = armorContext.WrapRequest(asReq); + + // Verify the wrapped request has PA_FX_FAST + Assert.IsNotNull(wrappedReq); + Assert.IsNotNull(wrappedReq.PaData); + + var paFast = wrappedReq.PaData.FirstOrDefault(p => p.Type == PaDataType.PA_FX_FAST); + Assert.IsNotNull(paFast, "Wrapped request should contain PA_FX_FAST"); + + // Verify the armor key was derived + Assert.IsNotNull(armorContext.ArmorKey, "Armor key should be derived after wrapping"); + + // Decode the PA_FX_FAST to verify structure + var fastRequest = KrbPaFxFastRequest.Decode(paFast.Value); + Assert.IsNotNull(fastRequest.ArmoredData); + Assert.IsNotNull(fastRequest.ArmoredData.Armor, "Armor should be present for AS-REQ"); + Assert.AreEqual(KrbArmorType.FX_FAST_ARMOR_AP_REQUEST, fastRequest.ArmoredData.Armor.Type); + Assert.IsNotNull(fastRequest.ArmoredData.RequestChecksum); + Assert.IsNotNull(fastRequest.ArmoredData.EncryptedFastRequest); + } + + [TestMethod] + public void FastArmorContext_ArmorKeyDerivation() + { + // Get an armor TGT + var (armorTgt, armorDecrypted) = GetArmorTgt(); + + var armorContext = new FastArmorContext(armorTgt, armorDecrypted); + + var cred = CreateCredential(); + var asReq = KrbAsReq.CreateAsReq(cred, AuthenticationOptions.AllAuthentication); + + armorContext.WrapRequest(asReq); + + // Verify armor key properties + Assert.IsNotNull(armorContext.ArmorKey); + Assert.AreEqual(armorDecrypted.Key.EType, armorContext.ArmorKey.EncryptionType); + } + + [TestMethod] + public void PaDataFastHandler_PreValidate_DecodesArmoredRequest() + { + // Get an armor TGT + var (armorTgt, armorDecrypted) = GetArmorTgt(); + + // Create client-side armor context and wrap a request + var armorContext = new FastArmorContext(armorTgt, armorDecrypted); + + var cred = CreateCredential(); + + var asReq = KrbAsReq.CreateAsReq(cred, AuthenticationOptions.AllAuthentication); + var wrappedReq = armorContext.WrapRequest(asReq); + + // Set up the server-side handler + var realmService = new FakeRealmService(Realm); + + var handler = new PaDataFastHandler(realmService); + + var preauth = new PreAuthenticationContext + { + Message = wrappedReq + }; + + // PreValidate should succeed and populate FastState + handler.PreValidate(preauth); + + var state = preauth.GetState(PaDataType.PA_FX_FAST); + + Assert.IsNotNull(state.ArmorKey, "Armor key should be derived"); + Assert.IsNotNull(state.InnerRequest, "Inner request should be decrypted"); + Assert.IsNotNull(state.DecryptedArmorApReq, "Armor AP-REQ should be decrypted"); + } + + [TestMethod] + public void PaDataFastHandler_PreValidate_NoFast_DoesNothing() + { + var realmService = new FakeRealmService(Realm); + var handler = new PaDataFastHandler(realmService); + + var cred = CreateCredential(); + var asReq = KrbAsReq.CreateAsReq(cred, AuthenticationOptions.AllAuthentication); + + var preauth = new PreAuthenticationContext + { + Message = asReq + }; + + // Should not throw - just returns without doing anything + handler.PreValidate(preauth); + + // FastState should not have an armor key + Assert.IsFalse( + preauth.PreAuthenticationState.ContainsKey(PaDataType.PA_FX_FAST), + "FastState should not be created when no FAST PA-Data is present" + ); + } + + [TestMethod] + public void FastState_Properties() + { + var state = new FastState(); + + Assert.IsNull(state.ArmorKey); + Assert.IsNull(state.InnerRequest); + Assert.IsNull(state.StrengthenKey); + Assert.IsNull(state.DecryptedArmorApReq); + } + + [TestMethod] + public void Cf2_ArmorKeyDerivation_Deterministic() + { + // Verify that Cf2 produces consistent results + var key1 = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }; + var key2 = new byte[] { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 }; + var pepper1 = Encoding.UTF8.GetBytes("subkeyarmor"); + var pepper2 = Encoding.UTF8.GetBytes("ticketarmor"); + + var result1 = KrbFx.Cf2(key1, key2, pepper1, pepper2, EncryptionType.AES128_CTS_HMAC_SHA1_96); + var result2 = KrbFx.Cf2(key1, key2, pepper1, pepper2, EncryptionType.AES128_CTS_HMAC_SHA1_96); + + Assert.IsTrue(result1.Span.SequenceEqual(result2.Span), "Cf2 should produce deterministic results"); + Assert.AreEqual(16, result1.Length, "AES128 key should be 16 bytes"); + } + + [TestMethod] + public void EncryptedChallengeHandler_NoFastArmor_Throws() + { + var realmService = new FakeRealmService(Realm); + var handler = new PaDataEncryptedChallengeHandler(realmService); + + var cred = CreateCredential(); + var asReq = KrbAsReq.CreateAsReq(cred, AuthenticationOptions.AllAuthentication); + + var preauth = new PreAuthenticationContext + { + Message = asReq, + Principal = realmService.Principals.Find( + KrbPrincipalName.FromString(Upn), Realm + ) + }; + + // Encrypted challenge without FAST armor should throw + Assert.ThrowsException(() => + { + handler.Validate(asReq, preauth); + }); + } + + [TestMethod] + public void WrapFastResponse_NullState_ReturnsNull() + { + var result = PaDataFastHandler.WrapFastResponse(null, null, null, null); + Assert.IsNull(result, "Should return null when no FAST state"); + } + + [TestMethod] + public void WrapFastError_NullState_ReturnsNull() + { + var result = PaDataFastHandler.WrapFastError(null, null); + Assert.IsNull(result, "Should return null when no FAST state"); + } + + [TestMethod] + public void WrapFastError_WithState_ProducesValidPaData() + { + var etype = EncryptionType.AES256_CTS_HMAC_SHA1_96; + var armorKeyData = KrbEncryptionKey.Generate(etype); + var armorKey = armorKeyData.AsKey(); + + var state = new FastState + { + ArmorKey = armorKey, + InnerRequest = new KrbFastReq + { + FastOptions = FastOptions.Reserved, + PaData = Array.Empty(), + ReqBody = new KrbKdcReqBody + { + Nonce = 12345 + } + } + }; + + var error = new KrbError + { + ErrorCode = KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED, + EText = "test error", + Realm = Realm, + SName = KrbPrincipalName.FromString("krbtgt/" + Realm) + }; + error.StampServerTime(); + + var result = PaDataFastHandler.WrapFastError(state, error); + + Assert.IsNotNull(result); + Assert.AreEqual(PaDataType.PA_FX_FAST, result.Type); + + // Verify the reply can be decoded + var reply = KrbPaFxFastReply.Decode(result.Value); + Assert.IsNotNull(reply.ArmoredData); + Assert.IsNotNull(reply.ArmoredData.EncFastRep); + + // Decrypt and verify + var fastResponse = reply.ArmoredData.EncFastRep.Decrypt( + armorKey, + KeyUsage.FastRep, + b => KrbFastResponse.Decode(b) + ); + + Assert.IsNotNull(fastResponse); + Assert.AreEqual(12345, fastResponse.Nonce); + Assert.IsNotNull(fastResponse.PaData); + Assert.IsTrue(fastResponse.PaData.Length > 0); + } + + [TestMethod] + public void KdcServer_RegistersFastHandlers_WhenEnabled() + { + var config = Krb5Config.Default(); + config.KdcDefaults.RegisterDefaultFastHandler = true; + + var options = new KdcServerOptions + { + DefaultRealm = Realm, + IsDebug = true, + RealmLocator = realm => new FakeRealmService(realm), + Configuration = config + }; + + var server = new KdcServer(options); + + // The server should have registered FAST handlers internally. + // We can verify this by trying to process a FAST-armored message. + // For now, just verify the server was created without errors. + Assert.IsNotNull(server); + } + } +} diff --git a/Tools/Asn1CodeGen/Asn1Ast.cs b/Tools/Asn1CodeGen/Asn1Ast.cs new file mode 100644 index 00000000..f0e17658 --- /dev/null +++ b/Tools/Asn1CodeGen/Asn1Ast.cs @@ -0,0 +1,152 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Kerberos.NET.Asn1CodeGen +{ + public class Asn1Module + { + public string Name { get; set; } + public string OidComponents { get; set; } + public TagDefault TagDefault { get; set; } = TagDefault.Explicit; + public List Imports { get; set; } = new(); + public List TypeAssignments { get; set; } = new(); + public Dictionary TypeAliases { get; set; } = new(); + } + + public enum TagDefault + { + Explicit, + Implicit, + Automatic + } + + public class Asn1Import + { + public string Module { get; set; } + public List Symbols { get; set; } = new(); + } + + public class Asn1TypeAssignment + { + public string Name { get; set; } + public Asn1Type Type { get; set; } + public string Comment { get; set; } + } + + public abstract class Asn1Type + { + } + + public class Asn1SequenceType : Asn1Type + { + public List Fields { get; set; } = new(); + public bool Extensible { get; set; } + } + + public class Asn1ChoiceType : Asn1Type + { + public List Fields { get; set; } = new(); + public bool Extensible { get; set; } + } + + public class Asn1SequenceOfType : Asn1Type + { + public Asn1Type ElementType { get; set; } + } + + public class Asn1SetOfType : Asn1Type + { + public Asn1Type ElementType { get; set; } + } + + public class Asn1TaggedType : Asn1Type + { + public TagClass TagClass { get; set; } = TagClass.ContextSpecific; + public int TagNumber { get; set; } + public TaggingMode Mode { get; set; } = TaggingMode.Default; + public Asn1Type InnerType { get; set; } + } + + public enum TagClass + { + Universal, + Application, + ContextSpecific, + Private + } + + public enum TaggingMode + { + Default, + Explicit, + Implicit + } + + public class Asn1Field + { + public string Name { get; set; } + public Asn1Type Type { get; set; } + public bool Optional { get; set; } + public string DefaultValue { get; set; } + } + + // Builtin types + public class Asn1BooleanType : Asn1Type { } + + public class Asn1IntegerType : Asn1Type + { + public List NamedNumbers { get; set; } + } + + public class Asn1NamedNumber + { + public string Name { get; set; } + public long Value { get; set; } + } + + public class Asn1BitStringType : Asn1Type + { + public List NamedBits { get; set; } + } + + public class Asn1OctetStringType : Asn1Type { } + public class Asn1NullType : Asn1Type { } + + public class Asn1ObjectIdentifierType : Asn1Type { } + + public class Asn1EnumeratedType : Asn1Type + { + public List Values { get; set; } = new(); + public bool Extensible { get; set; } + } + + public class Asn1StringType : Asn1Type + { + public Asn1StringKind Kind { get; set; } + } + + public enum Asn1StringKind + { + UTF8String, + PrintableString, + IA5String, + VisibleString, + GeneralString, + BMPString, + T61String + } + + public class Asn1GeneralizedTimeType : Asn1Type { } + public class Asn1UtcTimeType : Asn1Type { } + + public class Asn1ReferencedType : Asn1Type + { + public string ReferenceName { get; set; } + } + + public class Asn1AnyType : Asn1Type { } +} diff --git a/Tools/Asn1CodeGen/Asn1CodeGen.csproj b/Tools/Asn1CodeGen/Asn1CodeGen.csproj new file mode 100644 index 00000000..f35b0bb5 --- /dev/null +++ b/Tools/Asn1CodeGen/Asn1CodeGen.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + Kerberos.NET.Asn1CodeGen + 9 + false + false + + + + + + + diff --git a/Tools/Asn1CodeGen/Asn1Lexer.cs b/Tools/Asn1CodeGen/Asn1Lexer.cs new file mode 100644 index 00000000..58fc703a --- /dev/null +++ b/Tools/Asn1CodeGen/Asn1Lexer.cs @@ -0,0 +1,292 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Kerberos.NET.Asn1CodeGen +{ + public class Asn1Lexer + { + private static readonly Dictionary Keywords = new(StringComparer.Ordinal) + { + { "SEQUENCE", Asn1TokenType.SEQUENCE }, + { "SET", Asn1TokenType.SET }, + { "CHOICE", Asn1TokenType.CHOICE }, + { "OF", Asn1TokenType.OF }, + { "BOOLEAN", Asn1TokenType.BOOLEAN }, + { "INTEGER", Asn1TokenType.INTEGER }, + { "BIT", Asn1TokenType.BIT }, + { "STRING", Asn1TokenType.STRING }, + { "OCTET", Asn1TokenType.OCTET }, + { "NULL", Asn1TokenType.NULL }, + { "OBJECT", Asn1TokenType.OBJECT }, + { "IDENTIFIER", Asn1TokenType.IDENTIFIER }, + { "ENUMERATED", Asn1TokenType.ENUMERATED }, + { "OPTIONAL", Asn1TokenType.OPTIONAL }, + { "DEFAULT", Asn1TokenType.DEFAULT }, + { "IMPLICIT", Asn1TokenType.IMPLICIT }, + { "EXPLICIT", Asn1TokenType.EXPLICIT }, + { "TAGS", Asn1TokenType.TAGS }, + { "BEGIN", Asn1TokenType.BEGIN }, + { "END", Asn1TokenType.END }, + { "DEFINITIONS", Asn1TokenType.DEFINITIONS }, + { "IMPORTS", Asn1TokenType.IMPORTS }, + { "EXPORTS", Asn1TokenType.EXPORTS }, + { "FROM", Asn1TokenType.FROM }, + { "UNIVERSAL", Asn1TokenType.UNIVERSAL }, + { "APPLICATION", Asn1TokenType.APPLICATION }, + { "PRIVATE", Asn1TokenType.PRIVATE }, + { "AUTOMATIC", Asn1TokenType.AUTOMATIC }, + { "COMPONENTS", Asn1TokenType.COMPONENTS }, + { "COMPONENT", Asn1TokenType.COMPONENT }, + { "CONTAINING", Asn1TokenType.CONTAINING }, + { "TRUE", Asn1TokenType.TRUE }, + { "FALSE", Asn1TokenType.FALSE }, + { "SIZE", Asn1TokenType.SIZE }, + { "MIN", Asn1TokenType.MIN }, + { "MAX", Asn1TokenType.MAX }, + { "UNIQUE", Asn1TokenType.UNIQUE }, + { "WITH", Asn1TokenType.WITH }, + { "ABSTRACT-SYNTAX", Asn1TokenType.ABSTRACT_SYNTAX }, + { "TYPE-IDENTIFIER", Asn1TokenType.TYPE_IDENTIFIER }, + { "GeneralizedTime", Asn1TokenType.GeneralizedTime }, + { "UTCTime", Asn1TokenType.UTCTime }, + { "UTF8String", Asn1TokenType.UTF8String }, + { "PrintableString", Asn1TokenType.PrintableString }, + { "IA5String", Asn1TokenType.IA5String }, + { "VisibleString", Asn1TokenType.VisibleString }, + { "GeneralString", Asn1TokenType.GeneralString }, + { "BMPString", Asn1TokenType.BMPString }, + { "T61String", Asn1TokenType.T61String }, + { "ANY", Asn1TokenType.ANY }, + { "DEFINED", Asn1TokenType.DEFINED }, + { "BY", Asn1TokenType.BY }, + }; + + private readonly string source; + private int pos; + private int line = 1; + private int col = 1; + + public Asn1Lexer(string source) + { + this.source = source ?? throw new ArgumentNullException(nameof(source)); + } + + public static Asn1Lexer FromFile(string path) => new(File.ReadAllText(path)); + + public List Tokenize() + { + var tokens = new List(); + + while (this.pos < this.source.Length) + { + this.SkipWhitespace(); + + if (this.pos >= this.source.Length) + { + break; + } + + // Skip ASN.1 comments (-- ... -- or -- ... EOL) + if (this.Peek() == '-' && this.Peek(1) == '-') + { + this.SkipComment(); + continue; + } + + var token = this.ReadToken(); + + if (token != null) + { + tokens.Add(token); + } + } + + tokens.Add(new Asn1Token { Type = Asn1TokenType.EOF, Value = "", Line = this.line, Column = this.col }); + return tokens; + } + + private Asn1Token ReadToken() + { + int startLine = this.line; + int startCol = this.col; + char c = this.Peek(); + + // Multi-character symbols + if (c == ':' && this.Peek(1) == ':' && this.Peek(2) == '=') + { + this.Advance(3); + return new Asn1Token { Type = Asn1TokenType.Assign, Value = "::=", Line = startLine, Column = startCol }; + } + + if (c == '.' && this.Peek(1) == '.' && this.Peek(2) == '.') + { + this.Advance(3); + return new Asn1Token { Type = Asn1TokenType.DotDotDot, Value = "...", Line = startLine, Column = startCol }; + } + + if (c == '.' && this.Peek(1) == '.') + { + this.Advance(2); + return new Asn1Token { Type = Asn1TokenType.DotDot, Value = "..", Line = startLine, Column = startCol }; + } + + // Single-character symbols + switch (c) + { + case '{': this.Advance(); return new Asn1Token { Type = Asn1TokenType.LeftBrace, Value = "{", Line = startLine, Column = startCol }; + case '}': this.Advance(); return new Asn1Token { Type = Asn1TokenType.RightBrace, Value = "}", Line = startLine, Column = startCol }; + case '[': this.Advance(); return new Asn1Token { Type = Asn1TokenType.LeftBracket, Value = "[", Line = startLine, Column = startCol }; + case ']': this.Advance(); return new Asn1Token { Type = Asn1TokenType.RightBracket, Value = "]", Line = startLine, Column = startCol }; + case '(': this.Advance(); return new Asn1Token { Type = Asn1TokenType.LeftParen, Value = "(", Line = startLine, Column = startCol }; + case ')': this.Advance(); return new Asn1Token { Type = Asn1TokenType.RightParen, Value = ")", Line = startLine, Column = startCol }; + case ',': this.Advance(); return new Asn1Token { Type = Asn1TokenType.Comma, Value = ",", Line = startLine, Column = startCol }; + case '.': this.Advance(); return new Asn1Token { Type = Asn1TokenType.Dot, Value = ".", Line = startLine, Column = startCol }; + case ';': this.Advance(); return new Asn1Token { Type = Asn1TokenType.Semicolon, Value = ";", Line = startLine, Column = startCol }; + case '|': this.Advance(); return new Asn1Token { Type = Asn1TokenType.Pipe, Value = "|", Line = startLine, Column = startCol }; + } + + // String literal + if (c == '"') + { + return this.ReadStringLiteral(startLine, startCol); + } + + // Number + if (char.IsDigit(c)) + { + return this.ReadNumber(startLine, startCol); + } + + // Identifier or keyword (may contain hyphens like ABSTRACT-SYNTAX) + if (char.IsLetter(c)) + { + return this.ReadIdentifierOrKeyword(startLine, startCol); + } + + // Unknown character - skip + this.Advance(); + return null; + } + + private Asn1Token ReadStringLiteral(int startLine, int startCol) + { + this.Advance(); // skip opening " + var sb = new StringBuilder(); + + while (this.pos < this.source.Length && this.Peek() != '"') + { + sb.Append(this.Peek()); + this.Advance(); + } + + if (this.pos < this.source.Length) + { + this.Advance(); // skip closing " + } + + return new Asn1Token { Type = Asn1TokenType.String, Value = sb.ToString(), Line = startLine, Column = startCol }; + } + + private Asn1Token ReadNumber(int startLine, int startCol) + { + var sb = new StringBuilder(); + + while (this.pos < this.source.Length && char.IsDigit(this.Peek())) + { + sb.Append(this.Peek()); + this.Advance(); + } + + return new Asn1Token { Type = Asn1TokenType.Number, Value = sb.ToString(), Line = startLine, Column = startCol }; + } + + private Asn1Token ReadIdentifierOrKeyword(int startLine, int startCol) + { + var sb = new StringBuilder(); + + while (this.pos < this.source.Length && + (char.IsLetterOrDigit(this.Peek()) || this.Peek() == '-')) + { + sb.Append(this.Peek()); + this.Advance(); + } + + string word = sb.ToString(); + + if (Keywords.TryGetValue(word, out var keywordType)) + { + return new Asn1Token { Type = keywordType, Value = word, Line = startLine, Column = startCol }; + } + + return new Asn1Token { Type = Asn1TokenType.Identifier, Value = word, Line = startLine, Column = startCol }; + } + + private void SkipWhitespace() + { + while (this.pos < this.source.Length && char.IsWhiteSpace(this.Peek())) + { + if (this.Peek() == '\n') + { + this.line++; + this.col = 1; + } + else + { + this.col++; + } + + this.pos++; + } + } + + private void SkipComment() + { + // Skip "--" + this.Advance(2); + + while (this.pos < this.source.Length) + { + if (this.Peek() == '-' && this.Peek(1) == '-') + { + this.Advance(2); + return; + } + + if (this.Peek() == '\n') + { + this.line++; + this.col = 1; + this.pos++; + return; + } + + this.Advance(); + } + } + + private char Peek(int offset = 0) + { + int idx = this.pos + offset; + return idx < this.source.Length ? this.source[idx] : '\0'; + } + + private void Advance(int count = 1) + { + for (int i = 0; i < count; i++) + { + if (this.pos < this.source.Length) + { + this.col++; + this.pos++; + } + } + } + } +} diff --git a/Tools/Asn1CodeGen/Asn1Parser.cs b/Tools/Asn1CodeGen/Asn1Parser.cs new file mode 100644 index 00000000..809429f0 --- /dev/null +++ b/Tools/Asn1CodeGen/Asn1Parser.cs @@ -0,0 +1,799 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; + +namespace Kerberos.NET.Asn1CodeGen +{ + /// + /// Recursive descent parser for ASN.1 notation. + /// Produces an AST suitable for C# code generation. + /// Supports multiple modules per file. + /// + public class Asn1Parser + { + private readonly List tokens; + private int pos; + + public Asn1Parser(List tokens) + { + this.tokens = tokens ?? throw new ArgumentNullException(nameof(tokens)); + } + + /// + /// Parse a single module (legacy entry point). + /// + public Asn1Module Parse() + { + return this.ParseSingleModule(); + } + + /// + /// Parse all modules in the token stream. + /// + public List ParseAll() + { + var modules = new List(); + + while (this.Current.Type != Asn1TokenType.EOF) + { + var module = this.ParseSingleModule(); + modules.Add(module); + } + + return modules; + } + + private Asn1Module ParseSingleModule() + { + var module = new Asn1Module(); + + // ModuleName [{ OID }] DEFINITIONS [TagDefault] ::= BEGIN ... END + module.Name = this.Expect(Asn1TokenType.Identifier).Value; + + // Optional OID arc after module name + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + module.OidComponents = this.CaptureBalanced(Asn1TokenType.LeftBrace, Asn1TokenType.RightBrace); + } + + this.Expect(Asn1TokenType.DEFINITIONS); + + // Optional tag default + if (this.Current.Type == Asn1TokenType.AUTOMATIC) + { + this.Advance(); + this.Expect(Asn1TokenType.TAGS); + module.TagDefault = TagDefault.Automatic; + } + else if (this.Current.Type == Asn1TokenType.IMPLICIT) + { + this.Advance(); + this.Expect(Asn1TokenType.TAGS); + module.TagDefault = TagDefault.Implicit; + } + else if (this.Current.Type == Asn1TokenType.EXPLICIT) + { + this.Advance(); + this.Expect(Asn1TokenType.TAGS); + module.TagDefault = TagDefault.Explicit; + } + + this.Expect(Asn1TokenType.Assign); + this.Expect(Asn1TokenType.BEGIN); + + // Optional IMPORTS + if (this.Current.Type == Asn1TokenType.IMPORTS) + { + module.Imports = this.ParseImports(); + } + + // Optional EXPORTS (skip) + if (this.Current.Type == Asn1TokenType.EXPORTS) + { + this.SkipUntil(Asn1TokenType.Semicolon); + this.Advance(); // skip ; + } + + // Type and value assignments + while (this.Current.Type != Asn1TokenType.END && this.Current.Type != Asn1TokenType.EOF) + { + this.ParseAssignment(module); + } + + if (this.Current.Type == Asn1TokenType.END) + { + this.Advance(); + } + + return module; + } + + /// + /// Parse one assignment (type, value, or alias) and add to the module. + /// + private void ParseAssignment(Asn1Module module) + { + if (this.Current.Type != Asn1TokenType.Identifier) + { + this.Advance(); + return; + } + + string name = this.Current.Value; + this.Advance(); + + // Detect value assignments: name OBJECT IDENTIFIER ::= { ... } + if (this.Current.Type == Asn1TokenType.OBJECT) + { + this.Advance(); // OBJECT + this.Expect(Asn1TokenType.IDENTIFIER); // IDENTIFIER + this.Expect(Asn1TokenType.Assign); // ::= + this.SkipBalanced(Asn1TokenType.LeftBrace, Asn1TokenType.RightBrace); + return; + } + + // Detect integer value assignments: name INTEGER ::= number + if (this.Current.Type == Asn1TokenType.INTEGER && + this.Peek(1).Type == Asn1TokenType.Assign) + { + this.Advance(); // INTEGER + this.Advance(); // ::= + this.Advance(); // number + return; + } + + this.Expect(Asn1TokenType.Assign); + + var type = this.ParseType(); + + // Determine if this is a type alias or a real type assignment + if (this.IsTypeAlias(type)) + { + module.TypeAliases[name] = type; + } + else + { + module.TypeAssignments.Add(new Asn1TypeAssignment + { + Name = name, + Type = type + }); + } + } + + /// + /// A type alias is a simple reference, a constrained builtin, or SEQUENCE OF a reference. + /// Real types are SEQUENCE { fields }, CHOICE { fields }, or tagged versions of those. + /// + private bool IsTypeAlias(Asn1Type type) + { + // Direct reference: Realm ::= KerberosString + if (type is Asn1ReferencedType) + { + return true; + } + + // Simple builtins without structure: Int32 ::= INTEGER, MechType ::= OBJECT IDENTIFIER + if (type is Asn1IntegerType || type is Asn1OctetStringType || + type is Asn1ObjectIdentifierType || type is Asn1GeneralizedTimeType || + type is Asn1UtcTimeType || type is Asn1BooleanType || + type is Asn1StringType || type is Asn1NullType) + { + return true; + } + + // BIT STRING with named bits: KerberosFlags ::= BIT STRING (SIZE (32..MAX)) + if (type is Asn1BitStringType) + { + return true; + } + + // SEQUENCE OF a reference: METHOD-DATA ::= SEQUENCE OF PA-DATA + if (type is Asn1SequenceOfType seqOf && seqOf.ElementType is Asn1ReferencedType) + { + return true; + } + + return false; + } + + private List ParseImports() + { + this.Expect(Asn1TokenType.IMPORTS); + var imports = new List(); + + while (this.Current.Type != Asn1TokenType.Semicolon && this.Current.Type != Asn1TokenType.EOF) + { + var import = new Asn1Import(); + + // Read symbols until FROM + while (this.Current.Type != Asn1TokenType.FROM && this.Current.Type != Asn1TokenType.EOF) + { + if (this.Current.Type == Asn1TokenType.Identifier || IsTypeKeyword(this.Current.Type)) + { + import.Symbols.Add(this.Current.Value); + } + + this.Advance(); + } + + this.Expect(Asn1TokenType.FROM); + import.Module = this.Expect(Asn1TokenType.Identifier).Value; + + // Skip optional OID + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + this.SkipBalanced(Asn1TokenType.LeftBrace, Asn1TokenType.RightBrace); + } + + imports.Add(import); + } + + this.Expect(Asn1TokenType.Semicolon); + return imports; + } + + private Asn1Type ParseType() + { + // Check for tagged type: [tag] IMPLICIT/EXPLICIT Type + if (this.Current.Type == Asn1TokenType.LeftBracket) + { + return this.ParseTaggedType(); + } + + var type = this.ParseBuiltinOrReferencedType(); + + // Check for constraint (SIZE, etc.) - skip + if (this.Current.Type == Asn1TokenType.LeftParen) + { + this.SkipBalanced(Asn1TokenType.LeftParen, Asn1TokenType.RightParen); + } + + return type; + } + + private Asn1Type ParseTaggedType() + { + var tagged = new Asn1TaggedType(); + + this.Expect(Asn1TokenType.LeftBracket); + + // Optional tag class + if (this.Current.Type == Asn1TokenType.UNIVERSAL) + { + tagged.TagClass = TagClass.Universal; + this.Advance(); + } + else if (this.Current.Type == Asn1TokenType.APPLICATION) + { + tagged.TagClass = TagClass.Application; + this.Advance(); + } + else if (this.Current.Type == Asn1TokenType.PRIVATE) + { + tagged.TagClass = TagClass.Private; + this.Advance(); + } + + // Tag number + tagged.TagNumber = int.Parse(this.Expect(Asn1TokenType.Number).Value); + + this.Expect(Asn1TokenType.RightBracket); + + // Optional IMPLICIT or EXPLICIT + if (this.Current.Type == Asn1TokenType.IMPLICIT) + { + tagged.Mode = TaggingMode.Implicit; + this.Advance(); + } + else if (this.Current.Type == Asn1TokenType.EXPLICIT) + { + tagged.Mode = TaggingMode.Explicit; + this.Advance(); + } + + tagged.InnerType = this.ParseType(); + + return tagged; + } + + private Asn1Type ParseBuiltinOrReferencedType() + { + switch (this.Current.Type) + { + case Asn1TokenType.SEQUENCE: + return this.ParseSequenceType(); + + case Asn1TokenType.SET: + return this.ParseSetType(); + + case Asn1TokenType.CHOICE: + return this.ParseChoiceType(); + + case Asn1TokenType.BOOLEAN: + this.Advance(); + return new Asn1BooleanType(); + + case Asn1TokenType.INTEGER: + return this.ParseIntegerType(); + + case Asn1TokenType.BIT: + return this.ParseBitStringType(); + + case Asn1TokenType.OCTET: + this.Advance(); + this.Expect(Asn1TokenType.STRING); + return new Asn1OctetStringType(); + + case Asn1TokenType.NULL: + this.Advance(); + return new Asn1NullType(); + + case Asn1TokenType.OBJECT: + this.Advance(); + this.Expect(Asn1TokenType.IDENTIFIER); + return new Asn1ObjectIdentifierType(); + + case Asn1TokenType.ENUMERATED: + return this.ParseEnumeratedType(); + + case Asn1TokenType.ANY: + this.Advance(); + // Skip optional DEFINED BY + if (this.Current.Type == Asn1TokenType.DEFINED) + { + this.Advance(); + this.Expect(Asn1TokenType.BY); + this.Advance(); // skip identifier + } + return new Asn1AnyType(); + + case Asn1TokenType.GeneralizedTime: + this.Advance(); + return new Asn1GeneralizedTimeType(); + + case Asn1TokenType.UTCTime: + this.Advance(); + return new Asn1UtcTimeType(); + + case Asn1TokenType.UTF8String: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.UTF8String }; + + case Asn1TokenType.PrintableString: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.PrintableString }; + + case Asn1TokenType.IA5String: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.IA5String }; + + case Asn1TokenType.VisibleString: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.VisibleString }; + + case Asn1TokenType.GeneralString: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.GeneralString }; + + case Asn1TokenType.BMPString: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.BMPString }; + + case Asn1TokenType.T61String: + this.Advance(); + return new Asn1StringType { Kind = Asn1StringKind.T61String }; + + case Asn1TokenType.Identifier: + var name = this.Current.Value; + this.Advance(); + return new Asn1ReferencedType { ReferenceName = name }; + + default: + throw new Asn1ParseException( + $"Unexpected token {this.Current.Type} ({this.Current.Value}) at {this.Current.Line}:{this.Current.Column}" + ); + } + } + + private Asn1Type ParseSequenceType() + { + this.Expect(Asn1TokenType.SEQUENCE); + + // SEQUENCE SIZE (...) OF Type + if (this.Current.Type == Asn1TokenType.SIZE) + { + this.Advance(); // SIZE + this.SkipBalanced(Asn1TokenType.LeftParen, Asn1TokenType.RightParen); + this.Expect(Asn1TokenType.OF); + return new Asn1SequenceOfType { ElementType = this.ParseType() }; + } + + // SEQUENCE OF + if (this.Current.Type == Asn1TokenType.OF) + { + this.Advance(); + return new Asn1SequenceOfType { ElementType = this.ParseType() }; + } + + // SEQUENCE { ... } + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + var seq = new Asn1SequenceType(); + this.Expect(Asn1TokenType.LeftBrace); + seq.Fields = this.ParseFieldList(ref seq); + this.Expect(Asn1TokenType.RightBrace); + return seq; + } + + // Just SEQUENCE (bare reference) + return new Asn1SequenceType(); + } + + private List ParseFieldList(ref Asn1SequenceType seqParent) + { + var fields = new List(); + + while (this.Current.Type != Asn1TokenType.RightBrace && this.Current.Type != Asn1TokenType.EOF) + { + // Extension marker + if (this.Current.Type == Asn1TokenType.DotDotDot) + { + if (seqParent != null) + { + seqParent.Extensible = true; + } + + this.Advance(); + + // Skip comma after ... + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + + continue; + } + + // COMPONENTS OF (skip) + if (this.Current.Type == Asn1TokenType.COMPONENTS) + { + this.Advance(); // COMPONENTS + this.Expect(Asn1TokenType.OF); + this.Advance(); // type name + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + continue; + } + + var field = this.ParseField(); + fields.Add(field); + + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + } + + return fields; + } + + private Asn1Field ParseField() + { + var field = new Asn1Field + { + Name = this.Expect(Asn1TokenType.Identifier).Value, + Type = this.ParseType() + }; + + if (this.Current.Type == Asn1TokenType.OPTIONAL) + { + field.Optional = true; + this.Advance(); + } + else if (this.Current.Type == Asn1TokenType.DEFAULT) + { + this.Advance(); + // Read default value (simplified - just capture the token) + field.DefaultValue = this.Current.Value; + this.Advance(); + } + + return field; + } + + private Asn1Type ParseSetType() + { + this.Expect(Asn1TokenType.SET); + + // SET SIZE (...) OF Type + if (this.Current.Type == Asn1TokenType.SIZE) + { + this.Advance(); + this.SkipBalanced(Asn1TokenType.LeftParen, Asn1TokenType.RightParen); + this.Expect(Asn1TokenType.OF); + return new Asn1SetOfType { ElementType = this.ParseType() }; + } + + if (this.Current.Type == Asn1TokenType.OF) + { + this.Advance(); + return new Asn1SetOfType { ElementType = this.ParseType() }; + } + + // SET { ... } - same structure as SEQUENCE + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + var seq = new Asn1SequenceType(); + this.Expect(Asn1TokenType.LeftBrace); + seq.Fields = this.ParseFieldList(ref seq); + this.Expect(Asn1TokenType.RightBrace); + return seq; + } + + return new Asn1SequenceType(); + } + + private Asn1Type ParseChoiceType() + { + this.Expect(Asn1TokenType.CHOICE); + this.Expect(Asn1TokenType.LeftBrace); + + var choice = new Asn1ChoiceType(); + + while (this.Current.Type != Asn1TokenType.RightBrace && this.Current.Type != Asn1TokenType.EOF) + { + if (this.Current.Type == Asn1TokenType.DotDotDot) + { + choice.Extensible = true; + this.Advance(); + + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + + continue; + } + + var field = this.ParseField(); + choice.Fields.Add(field); + + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + } + + this.Expect(Asn1TokenType.RightBrace); + return choice; + } + + private Asn1Type ParseIntegerType() + { + this.Expect(Asn1TokenType.INTEGER); + + var intType = new Asn1IntegerType(); + + // Optional named numbers: INTEGER { name(value), ... } + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + intType.NamedNumbers = this.ParseNamedNumbers(); + } + + // Optional constraint + if (this.Current.Type == Asn1TokenType.LeftParen) + { + this.SkipBalanced(Asn1TokenType.LeftParen, Asn1TokenType.RightParen); + } + + return intType; + } + + private Asn1Type ParseBitStringType() + { + this.Expect(Asn1TokenType.BIT); + this.Expect(Asn1TokenType.STRING); + + var bitString = new Asn1BitStringType(); + + // Optional named bits + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + bitString.NamedBits = this.ParseNamedNumbers(); + } + + // Optional constraint + if (this.Current.Type == Asn1TokenType.LeftParen) + { + this.SkipBalanced(Asn1TokenType.LeftParen, Asn1TokenType.RightParen); + } + + return bitString; + } + + private Asn1Type ParseEnumeratedType() + { + this.Expect(Asn1TokenType.ENUMERATED); + + var enumType = new Asn1EnumeratedType(); + + if (this.Current.Type == Asn1TokenType.LeftBrace) + { + this.Advance(); + long nextValue = 0; + + while (this.Current.Type != Asn1TokenType.RightBrace && this.Current.Type != Asn1TokenType.EOF) + { + if (this.Current.Type == Asn1TokenType.DotDotDot) + { + enumType.Extensible = true; + this.Advance(); + + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + + continue; + } + + var name = this.Expect(Asn1TokenType.Identifier).Value; + long value = nextValue; + + if (this.Current.Type == Asn1TokenType.LeftParen) + { + this.Advance(); + value = long.Parse(this.Expect(Asn1TokenType.Number).Value); + this.Expect(Asn1TokenType.RightParen); + } + + enumType.Values.Add(new Asn1NamedNumber { Name = name, Value = value }); + nextValue = value + 1; + + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + } + + this.Expect(Asn1TokenType.RightBrace); + } + + return enumType; + } + + private List ParseNamedNumbers() + { + var numbers = new List(); + this.Expect(Asn1TokenType.LeftBrace); + + while (this.Current.Type != Asn1TokenType.RightBrace && this.Current.Type != Asn1TokenType.EOF) + { + var name = this.Expect(Asn1TokenType.Identifier).Value; + this.Expect(Asn1TokenType.LeftParen); + var value = long.Parse(this.Expect(Asn1TokenType.Number).Value); + this.Expect(Asn1TokenType.RightParen); + + numbers.Add(new Asn1NamedNumber { Name = name, Value = value }); + + if (this.Current.Type == Asn1TokenType.Comma) + { + this.Advance(); + } + } + + this.Expect(Asn1TokenType.RightBrace); + return numbers; + } + + #region Helpers + + private Asn1Token Current => this.pos < this.tokens.Count ? this.tokens[this.pos] : this.tokens[this.tokens.Count - 1]; + + private Asn1Token Peek(int offset) + { + int idx = this.pos + offset; + return idx < this.tokens.Count ? this.tokens[idx] : this.tokens[this.tokens.Count - 1]; + } + + private void Advance() + { + if (this.pos < this.tokens.Count - 1) + { + this.pos++; + } + } + + private Asn1Token Expect(Asn1TokenType type) + { + if (this.Current.Type != type) + { + throw new Asn1ParseException( + $"Expected {type} but got {this.Current.Type} ({this.Current.Value}) at {this.Current.Line}:{this.Current.Column}" + ); + } + + var token = this.Current; + this.Advance(); + return token; + } + + private void SkipUntil(Asn1TokenType type) + { + while (this.Current.Type != type && this.Current.Type != Asn1TokenType.EOF) + { + this.Advance(); + } + } + + private void SkipBalanced(Asn1TokenType open, Asn1TokenType close) + { + int depth = 0; + + do + { + if (this.Current.Type == open) + { + depth++; + } + else if (this.Current.Type == close) + { + depth--; + } + + this.Advance(); + } + while (depth > 0 && this.Current.Type != Asn1TokenType.EOF); + } + + private string CaptureBalanced(Asn1TokenType open, Asn1TokenType close) + { + var parts = new List(); + int depth = 0; + + do + { + if (this.Current.Type == open) + { + depth++; + } + else if (this.Current.Type == close) + { + depth--; + } + + parts.Add(this.Current.Value); + this.Advance(); + } + while (depth > 0 && this.Current.Type != Asn1TokenType.EOF); + + return string.Join(" ", parts); + } + + private static bool IsTypeKeyword(Asn1TokenType type) => + type == Asn1TokenType.BOOLEAN || + type == Asn1TokenType.INTEGER || + type == Asn1TokenType.BIT || + type == Asn1TokenType.OCTET || + type == Asn1TokenType.NULL || + type == Asn1TokenType.OBJECT || + type == Asn1TokenType.ENUMERATED || + type == Asn1TokenType.SEQUENCE || + type == Asn1TokenType.SET || + type == Asn1TokenType.CHOICE; + + #endregion + } + + public class Asn1ParseException : Exception + { + public Asn1ParseException(string message) : base(message) { } + } +} diff --git a/Tools/Asn1CodeGen/Asn1Token.cs b/Tools/Asn1CodeGen/Asn1Token.cs new file mode 100644 index 00000000..5fcd97e6 --- /dev/null +++ b/Tools/Asn1CodeGen/Asn1Token.cs @@ -0,0 +1,97 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Asn1CodeGen +{ + public enum Asn1TokenType + { + // Literals + Identifier, + Number, + String, + + // Symbols + Assign, // ::= + LeftBrace, // { + RightBrace, // } + LeftBracket, // [ + RightBracket, // ] + LeftParen, // ( + RightParen, // ) + Comma, // , + Dot, // . + DotDotDot, // ... + DotDot, // .. + Semicolon, // ; + Pipe, // | + + // Keywords + SEQUENCE, + SET, + CHOICE, + OF, + BOOLEAN, + INTEGER, + BIT, + STRING, + OCTET, + NULL, + OBJECT, + IDENTIFIER, // as in OBJECT IDENTIFIER + ENUMERATED, + OPTIONAL, + DEFAULT, + IMPLICIT, + EXPLICIT, + TAGS, + BEGIN, + END, + DEFINITIONS, + IMPORTS, + EXPORTS, + FROM, + UNIVERSAL, + APPLICATION, + PRIVATE, + AUTOMATIC, + COMPONENT, + COMPONENTS, + CONTAINING, + TRUE, + FALSE, + SIZE, + MIN, + MAX, + UNIQUE, + WITH, + ABSTRACT_SYNTAX, + TYPE_IDENTIFIER, + GeneralizedTime, + UTCTime, + UTF8String, + PrintableString, + IA5String, + VisibleString, + GeneralString, + BMPString, + T61String, + ANY, + DEFINED, + BY, + + // End of file + EOF + } + + public class Asn1Token + { + public Asn1TokenType Type { get; set; } + public string Value { get; set; } + public int Line { get; set; } + public int Column { get; set; } + + public override string ToString() => $"{Type}({Value}) at {Line}:{Column}"; + } +} diff --git a/Tools/Asn1CodeGen/CSharpCodeEmitter.cs b/Tools/Asn1CodeGen/CSharpCodeEmitter.cs new file mode 100644 index 00000000..56a0041d --- /dev/null +++ b/Tools/Asn1CodeGen/CSharpCodeEmitter.cs @@ -0,0 +1,1638 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Kerberos.NET.Asn1CodeGen +{ + public class TypeConfig + { + public string CSharpName { get; set; } + public string Namespace { get; set; } = "Kerberos.NET.Entities"; + public int? ApplicationTag { get; set; } + public string InheritsFrom { get; set; } + public Dictionary Fields { get; set; } = new(); + } + + public class FieldConfig + { + public string CSharpName { get; set; } + public string BackingType { get; set; } + public string EnumType { get; set; } + public bool TreatAsEnum { get; set; } + } + + public class EmitterConfig + { + public Dictionary Types { get; set; } = new(); + } + + public class CSharpCodeEmitter + { + private readonly List modules; + private readonly EmitterConfig config; + + // Merged alias table from all modules + private readonly Dictionary aliases = new(); + + // Module-level tag defaults + private readonly Dictionary moduleTagDefaults = new(); + + // Type assignments that are SEQUENCE OF with inline SEQUENCE + // When referenced as a field, these should be treated as ClassName[] + private readonly HashSet sequenceOfInlineTypes = new(); + + public CSharpCodeEmitter(List modules, EmitterConfig config = null) + { + this.modules = modules; + this.config = config ?? new EmitterConfig(); + this.BuildAliasTable(); + } + + // Backward-compat single-module constructor + public CSharpCodeEmitter(Asn1Module module, EmitterConfig config = null) + : this(new List { module }, config) + { + } + + private void BuildAliasTable() + { + foreach (var module in this.modules) + { + foreach (var kvp in module.TypeAliases) + { + this.aliases[kvp.Key] = kvp.Value; + } + + // Track SEQUENCE OF SEQUENCE { ... } type assignments + foreach (var assignment in module.TypeAssignments) + { + var type = assignment.Type; + + // Unwrap APPLICATION tag if present + if (type is Asn1TaggedType tagged && tagged.TagClass == TagClass.Application) + { + type = tagged.InnerType; + } + + if (type is Asn1SequenceOfType seqOf && seqOf.ElementType is Asn1SequenceType) + { + this.sequenceOfInlineTypes.Add(assignment.Name); + } + } + + this.moduleTagDefaults[module.Name] = module.TagDefault; + } + } + + public Dictionary EmitAll() + { + var files = new Dictionary(); + + foreach (var module in this.modules) + { + foreach (var assignment in module.TypeAssignments) + { + var tc = this.GetTypeConfig(assignment.Name); + var className = tc.CSharpName ?? ToPascalCase(assignment.Name); + + // Handle SEQUENCE OF with inline SEQUENCE: + // e.g. AuthorizationData ::= SEQUENCE OF SEQUENCE { ... } + var emitType = this.UnwrapApplicationTag(assignment.Type, tc); + + if (emitType is Asn1SequenceOfType seqOf && seqOf.ElementType is Asn1SequenceType innerSeq) + { + // Emit the inner SEQUENCE as the class, the outer is just TypeName[] + var innerAssignment = new Asn1TypeAssignment + { + Name = assignment.Name, + Type = innerSeq, + Comment = assignment.Comment + }; + + var code = this.EmitTypeAssignment(innerAssignment, module); + files[className + ".generated.cs"] = code; + } + else + { + var code = this.EmitTypeAssignment(assignment, module); + files[className + ".generated.cs"] = code; + } + } + } + + return files; + } + + private Asn1Type UnwrapApplicationTag(Asn1Type type, TypeConfig tc) + { + if (type is Asn1TaggedType topTag && topTag.TagClass == TagClass.Application) + { + if (!tc.ApplicationTag.HasValue) + { + tc.ApplicationTag = topTag.TagNumber; + } + + return topTag.InnerType; + } + + return type; + } + + public string EmitTypeAssignment(Asn1TypeAssignment assignment, Asn1Module module = null) + { + var tc = this.GetTypeConfig(assignment.Name); + var className = tc.CSharpName ?? ToPascalCase(assignment.Name); + var ns = tc.Namespace; + + var emitType = this.UnwrapApplicationTag(assignment.Type, tc); + + // Determine what usings we need + var usings = new HashSet + { + "System", + "System.Security.Cryptography", + "System.Security.Cryptography.Asn1", + "Kerberos.NET.Crypto", + "Kerberos.NET.Asn1", + }; + + if (this.NeedsCollections(emitType)) + { + usings.Add("System.Collections.Generic"); + } + + if (emitType is Asn1ChoiceType) + { + usings.Add("System.Runtime.InteropServices"); + } + + var sb = new StringBuilder(); + this.EmitHeader(sb); + + foreach (var u in usings.OrderBy(x => x)) + { + sb.AppendLine($"using {u};"); + } + + sb.AppendLine(); + sb.AppendLine($"namespace {ns}"); + sb.AppendLine("{"); + + if (tc.InheritsFrom != null) + { + this.EmitInheritedSequence(sb, assignment, tc); + } + else if (emitType is Asn1ChoiceType choice) + { + this.EmitChoiceType(sb, assignment, choice, tc, className, module); + } + else if (emitType is Asn1SequenceType seq) + { + this.EmitSequenceType(sb, assignment, seq, tc, className, module); + } + + sb.AppendLine("}"); + return sb.ToString(); + } + + private void EmitHeader(StringBuilder sb) + { + sb.AppendLine("// -----------------------------------------------------------------------"); + sb.AppendLine("// Licensed to The .NET Foundation under one or more agreements."); + sb.AppendLine("// The .NET Foundation licenses this file to you under the MIT license."); + sb.AppendLine("// -----------------------------------------------------------------------"); + sb.AppendLine(); + sb.AppendLine("// This is a generated file."); + sb.AppendLine("// The generation template has been modified from .NET Runtime implementation"); + sb.AppendLine(); + } + + #region Type Alias Resolution + + /// + /// Resolve a referenced type name through the alias chain to a concrete Asn1Type. + /// + private Asn1Type ResolveAlias(string name) + { + var visited = new HashSet(); + + while (this.aliases.TryGetValue(name, out var aliased)) + { + if (!visited.Add(name)) + { + break; // cycle + } + + if (aliased is Asn1ReferencedType refType) + { + name = refType.ReferenceName; + } + else + { + return aliased; + } + } + + return null; + } + + /// + /// Resolve a field's type, chasing through aliases and returning the resolved + /// Asn1Type for encode/decode code generation. + /// + private Asn1Type ResolveFieldType(Asn1Type type) + { + if (type is Asn1ReferencedType refType) + { + // SEQUENCE OF SEQUENCE { ... } type assignments should be treated as arrays + if (this.sequenceOfInlineTypes.Contains(refType.ReferenceName)) + { + return new Asn1SequenceOfType + { + ElementType = new Asn1ReferencedType { ReferenceName = refType.ReferenceName } + }; + } + + var resolved = this.ResolveAlias(refType.ReferenceName); + + if (resolved != null) + { + return resolved; + } + } + + return type; + } + + /// + /// Check if a referenced type ultimately resolves to a generatable class + /// (as opposed to a builtin type alias). + /// + private bool IsGeneratableReference(string name) + { + var resolved = this.ResolveAlias(name); + return resolved == null; // no alias means it's a real type with its own class + } + + #endregion + + #region Tagging + + private TaggingMode GetEffectiveTaggingMode(Asn1TaggedType tagged, Asn1Module module) + { + if (tagged.Mode != TaggingMode.Default) + { + return tagged.Mode; + } + + // Use module default + if (module != null) + { + return module.TagDefault == TagDefault.Implicit + ? TaggingMode.Implicit + : TaggingMode.Explicit; + } + + return TaggingMode.Explicit; + } + + private bool IsImplicitTag(Asn1Field field, Asn1Module module) + { + if (field.Type is Asn1TaggedType tagged) + { + return this.GetEffectiveTaggingMode(tagged, module) == TaggingMode.Implicit; + } + + return false; + } + + #endregion + + #region Sequence Type + + private void EmitSequenceType( + StringBuilder sb, + Asn1TypeAssignment assignment, + Asn1SequenceType seq, + TypeConfig tc, + string className, + Asn1Module module) + { + bool hasAppTag = tc.ApplicationTag.HasValue; + + sb.AppendLine($" public partial class {className}"); + sb.AppendLine(" {"); + + if (!string.IsNullOrEmpty(assignment.Comment)) + { + sb.AppendLine(" /*"); + foreach (var line in assignment.Comment.Split('\n')) + { + sb.AppendLine($" {line.TrimEnd()}"); + } + + sb.AppendLine(" */"); + } + + sb.AppendLine(" "); + + // Properties + foreach (var field in seq.Fields) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + var propType = this.GetCSharpType(field, fc); + + sb.AppendLine($" public {propType} {propName} {{ get; set; }}"); + sb.AppendLine(" "); + } + + // Encoding + sb.AppendLine(" // Encoding methods"); + + if (!hasAppTag) + { + sb.AppendLine(" public ReadOnlyMemory Encode()"); + sb.AppendLine(" {"); + sb.AppendLine(" var writer = new AsnWriter(AsnEncodingRules.DER);"); + sb.AppendLine(); + sb.AppendLine(" Encode(writer);"); + sb.AppendLine(); + sb.AppendLine(" return writer.EncodeAsMemory();"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + } + + sb.AppendLine(" internal void Encode(AsnWriter writer)"); + sb.AppendLine(" {"); + sb.AppendLine(hasAppTag + ? " EncodeApplication(writer, ApplicationTag);" + : " Encode(writer, Asn1Tag.Sequence);"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + + sb.AppendLine(" internal void Encode(AsnWriter writer, Asn1Tag tag)"); + sb.AppendLine(" {"); + sb.AppendLine(" writer.PushSequence(tag);"); + sb.AppendLine(" "); + + foreach (var field in seq.Fields) + { + this.EmitFieldEncode(sb, field, tc, module); + } + + sb.AppendLine(" writer.PopSequence(tag);"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + + sb.AppendLine(" internal void EncodeApplication(AsnWriter writer, Asn1Tag tag)"); + sb.AppendLine(" {"); + sb.AppendLine(" writer.PushSequence(tag);"); + sb.AppendLine(" "); + sb.AppendLine(" this.Encode(writer, Asn1Tag.Sequence);"); + sb.AppendLine(" "); + sb.AppendLine(" writer.PopSequence(tag);"); + sb.AppendLine(" } "); + sb.AppendLine(" "); + + if (hasAppTag) + { + int appTagNum = tc.ApplicationTag.Value; + sb.AppendLine($" private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, {appTagNum});"); + sb.AppendLine(" "); + sb.AppendLine(" public virtual ReadOnlyMemory EncodeApplication() "); + sb.AppendLine(" {"); + sb.AppendLine(" return EncodeApplication(ApplicationTag);"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + sb.AppendLine($" public static {className} DecodeApplication(ReadOnlyMemory encoded)"); + sb.AppendLine(" {"); + sb.AppendLine(" AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER);"); + sb.AppendLine(); + sb.AppendLine(" var sequence = reader.ReadSequence(ApplicationTag);"); + sb.AppendLine(" "); + sb.AppendLine($" {className} decoded;"); + sb.AppendLine(" Decode(sequence, Asn1Tag.Sequence, out decoded);"); + sb.AppendLine(" sequence.ThrowIfNotEmpty();"); + sb.AppendLine(); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + sb.AppendLine($" internal static {className} DecodeApplication(AsnReader reader, out T decoded)"); + sb.AppendLine($" where T: {className}, new()"); + sb.AppendLine(" {"); + sb.AppendLine(" var sequence = reader.ReadSequence(ApplicationTag);"); + sb.AppendLine(" "); + sb.AppendLine(" Decode(sequence, Asn1Tag.Sequence, out decoded);"); + sb.AppendLine(" sequence.ThrowIfNotEmpty();"); + sb.AppendLine(); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + } + else + { + sb.AppendLine(" public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory();"); + } + + sb.AppendLine(" "); + sb.AppendLine(" internal ReadOnlyMemory EncodeApplication(Asn1Tag tag)"); + sb.AppendLine(" {"); + sb.AppendLine(" using (var writer = new AsnWriter(AsnEncodingRules.DER))"); + sb.AppendLine(" {"); + sb.AppendLine(" EncodeApplication(writer, tag);"); + sb.AppendLine(); + sb.AppendLine(" return writer.EncodeAsMemory();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + + // Decode methods + if (!hasAppTag) + { + sb.AppendLine($" public static {className} Decode(ReadOnlyMemory data)"); + sb.AppendLine(" {"); + sb.AppendLine(" return Decode(data, AsnEncodingRules.DER);"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" internal static {className} Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet)"); + sb.AppendLine(" {"); + sb.AppendLine(" return Decode(Asn1Tag.Sequence, encoded, ruleSet);"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + } + + sb.AppendLine($" internal static {className} Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded)"); + sb.AppendLine(" {"); + sb.AppendLine(" AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER);"); + sb.AppendLine(" "); + sb.AppendLine($" Decode(reader, expectedTag, out {className} decoded);"); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" internal static {className} Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet)"); + sb.AppendLine(" {"); + sb.AppendLine(" AsnReader reader = new AsnReader(encoded, ruleSet);"); + sb.AppendLine(" "); + sb.AppendLine($" Decode(reader, expectedTag, out {className} decoded);"); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + sb.AppendLine(); + + sb.AppendLine($" internal static void Decode(AsnReader reader, out T decoded)"); + sb.AppendLine($" where T: {className}, new()"); + sb.AppendLine(" {"); + sb.AppendLine(" if (reader == null)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new ArgumentNullException(nameof(reader));"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + sb.AppendLine(hasAppTag + ? " DecodeApplication(reader, out decoded);" + : " Decode(reader, Asn1Tag.Sequence, out decoded);"); + sb.AppendLine(" }"); + sb.AppendLine(); + + sb.AppendLine($" internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded)"); + sb.AppendLine($" where T: {className}, new()"); + sb.AppendLine(" {"); + sb.AppendLine(" if (reader == null)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new ArgumentNullException(nameof(reader));"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" decoded = new T();"); + sb.AppendLine(" "); + sb.AppendLine(" AsnReader sequenceReader = reader.ReadSequence(expectedTag);"); + + bool needsExplicitReader = seq.Fields.Count > 0; + bool needsCollectionReader = seq.Fields.Any(f => this.IsSequenceOfField(f) || this.IsSetOfField(f)); + + if (needsExplicitReader) + { + sb.AppendLine(" AsnReader explicitReader;"); + } + + if (needsCollectionReader) + { + sb.AppendLine(" AsnReader collectionReader;"); + } + + sb.AppendLine(" "); + + foreach (var field in seq.Fields) + { + this.EmitFieldDecode(sb, field, tc, className, module); + } + + sb.AppendLine(" sequenceReader.ThrowIfNotEmpty();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + #endregion + + #region Choice Type + + private void EmitChoiceType( + StringBuilder sb, + Asn1TypeAssignment assignment, + Asn1ChoiceType choice, + TypeConfig tc, + string className, + Asn1Module module) + { + sb.AppendLine($" public partial class {className}"); + sb.AppendLine(" {"); + + if (!string.IsNullOrEmpty(assignment.Comment)) + { + sb.AppendLine(" /*"); + foreach (var line in assignment.Comment.Split('\n')) + { + sb.AppendLine($" {line.TrimEnd()}"); + } + + sb.AppendLine(" */"); + } + + sb.AppendLine(" "); + + foreach (var field in choice.Fields) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + var propType = this.GetChoiceFieldCSharpType(field, fc); + sb.AppendLine($" public {propType} {propName} {{ get; set; }}"); + sb.AppendLine(" "); + } + + // DEBUG tag validation + sb.AppendLine("#if DEBUG"); + sb.AppendLine($" static {className}()"); + sb.AppendLine(" {"); + sb.AppendLine(" var usedTags = new System.Collections.Generic.Dictionary();"); + sb.AppendLine(" Action ensureUniqueTag = (tag, fieldName) =>"); + sb.AppendLine(" {"); + sb.AppendLine(" if (usedTags.TryGetValue(tag, out string existing))"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new InvalidOperationException($\"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'\");"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" usedTags.Add(tag, fieldName);"); + sb.AppendLine(" };"); + sb.AppendLine(" "); + + foreach (var field in choice.Fields) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + int tagNum = this.GetTagNumber(field); + sb.AppendLine($" ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, {tagNum}), \"{propName}\");"); + } + + sb.AppendLine(" }"); + sb.AppendLine("#endif"); + + sb.AppendLine(" // Encoding methods"); + sb.AppendLine(" public ReadOnlyMemory Encode()"); + sb.AppendLine(" {"); + sb.AppendLine(" var writer = new AsnWriter(AsnEncodingRules.DER);"); + sb.AppendLine(); + sb.AppendLine(" Encode(writer);"); + sb.AppendLine(); + sb.AppendLine(" return writer.EncodeAsMemory();"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" internal void Encode(AsnWriter writer)"); + sb.AppendLine(" {"); + sb.AppendLine(" bool wroteValue = false; "); + sb.AppendLine(" "); + + foreach (var field in choice.Fields) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + int tagNum = this.GetTagNumber(field); + bool isImplicit = this.IsImplicitTag(field, module); + var innerType = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(innerType); + + sb.AppendLine($" if (Asn1Extension.HasValue({propName}))"); + sb.AppendLine(" {"); + sb.AppendLine(" if (wroteValue)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new CryptographicException();"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + + // CHOICE fields always use explicit wrapping (PushSequence/PopSequence) + sb.AppendLine($" writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + if (this.IsPrimitiveType(resolved)) + { + this.EmitFieldEncodeValue(sb, resolved, propName, fc, " ", true); + } + else + { + sb.AppendLine($" {propName}?.Encode(writer);"); + } + sb.AppendLine($" writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + + sb.AppendLine(" wroteValue = true;"); + sb.AppendLine(" }"); + } + + sb.AppendLine(" if (!wroteValue)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new CryptographicException();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + + sb.AppendLine(" "); + sb.AppendLine(" internal ReadOnlyMemory EncodeApplication(Asn1Tag tag)"); + sb.AppendLine(" {"); + sb.AppendLine(" using (var writer = new AsnWriter(AsnEncodingRules.DER))"); + sb.AppendLine(" {"); + sb.AppendLine(" writer.PushSequence(tag);"); + sb.AppendLine(" "); + sb.AppendLine(" this.Encode(writer);"); + sb.AppendLine(); + sb.AppendLine(" writer.PopSequence(tag);"); + sb.AppendLine(); + sb.AppendLine(" return writer.EncodeAsMemory();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + + sb.AppendLine($" public static {className} Decode(ReadOnlyMemory data)"); + sb.AppendLine(" {"); + sb.AppendLine(" return Decode(data, AsnEncodingRules.DER);"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" internal static {className} Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet)"); + sb.AppendLine(" {"); + sb.AppendLine(" AsnReader reader = new AsnReader(encoded, ruleSet);"); + sb.AppendLine(" "); + sb.AppendLine($" Decode(reader, out {className} decoded);"); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" internal static void Decode(AsnReader reader, out T decoded)"); + sb.AppendLine($" where T: {className}, new()"); + sb.AppendLine(" {"); + sb.AppendLine(" if (reader == null)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new ArgumentNullException(nameof(reader));"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" decoded = new T();"); + sb.AppendLine(" "); + sb.AppendLine(" Asn1Tag tag = reader.PeekTag();"); + sb.AppendLine(" AsnReader explicitReader;"); + sb.AppendLine(" "); + + bool first = true; + + foreach (var field in choice.Fields) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + int tagNum = this.GetTagNumber(field); + bool isImplicit = this.IsImplicitTag(field, module); + var innerType = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(innerType); + + string keyword = first ? "if" : "else if"; + first = false; + + sb.AppendLine($" {keyword} (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, {tagNum})))"); + sb.AppendLine(" {"); + + // CHOICE fields always use explicit wrapping + sb.AppendLine($" explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + if (this.IsPrimitiveType(resolved)) + { + this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", false); + } + else + { + string typeName = this.GetReferencedTypeName(field); + sb.AppendLine($" {typeName}.Decode<{typeName}>(explicitReader, out {typeName} tmp{propName});"); + sb.AppendLine($" decoded.{propName} = tmp{propName};"); + } + sb.AppendLine(" explicitReader.ThrowIfNotEmpty();"); + + sb.AppendLine(" }"); + } + + sb.AppendLine(" else"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new CryptographicException();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + #endregion + + #region Inherited Sequence + + private void EmitInheritedSequence(StringBuilder sb, Asn1TypeAssignment assignment, TypeConfig tc) + { + string className = tc.CSharpName ?? ToPascalCase(assignment.Name); + string baseName = tc.InheritsFrom; + int appTag = tc.ApplicationTag ?? 0; + + sb.AppendLine($" public partial class {className} : {baseName}"); + sb.AppendLine(" {"); + + if (!string.IsNullOrEmpty(assignment.Comment)) + { + sb.AppendLine(" /*"); + foreach (var line in assignment.Comment.Split('\n')) + { + sb.AppendLine($" {line.TrimEnd()}"); + } + + sb.AppendLine(" */"); + } + + sb.AppendLine(" "); + sb.AppendLine($" private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, {appTag});"); + sb.AppendLine(" "); + sb.AppendLine(" public override ReadOnlyMemory EncodeApplication() "); + sb.AppendLine(" {"); + sb.AppendLine(" return EncodeApplication(ApplicationTag);"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + sb.AppendLine($" public static {className} DecodeApplication(ReadOnlyMemory encoded)"); + sb.AppendLine(" {"); + sb.AppendLine(" AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER);"); + sb.AppendLine(); + sb.AppendLine(" var sequence = reader.ReadSequence(ApplicationTag);"); + sb.AppendLine(" "); + sb.AppendLine($" {className} decoded;"); + sb.AppendLine(" Decode(sequence, out decoded);"); + sb.AppendLine(" sequence.ThrowIfNotEmpty();"); + sb.AppendLine(); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + #endregion + + #region Field Encoding + + private void EmitFieldEncode(StringBuilder sb, Asn1Field field, TypeConfig tc, Asn1Module module) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + int tagNum = this.GetTagNumber(field); + var innerType = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(innerType); + bool hasTag = field.Type is Asn1TaggedType; + bool isImplicit = this.IsImplicitTag(field, module); + + if (field.Optional) + { + sb.AppendLine(); + sb.AppendLine($" if (Asn1Extension.HasValue({propName}))"); + sb.AppendLine(" {"); + + if (hasTag && isImplicit && this.IsPrimitiveType(resolved)) + { + this.EmitImplicitPrimitiveEncode(sb, resolved, propName, fc, tagNum, " ", optional: true); + } + else if (hasTag) + { + sb.AppendLine($" writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + this.EmitFieldEncodeValue(sb, resolved, propName, fc, " ", true); + sb.AppendLine($" writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + } + else + { + this.EmitFieldEncodeValue(sb, resolved, propName, fc, " ", true); + } + + sb.AppendLine(" }"); + } + else + { + if (hasTag && isImplicit && this.IsPrimitiveType(resolved)) + { + this.EmitImplicitPrimitiveEncode(sb, resolved, propName, fc, tagNum, " "); + } + else if (hasTag) + { + sb.AppendLine($" writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + this.EmitFieldEncodeValue(sb, resolved, propName, fc, " ", false); + sb.AppendLine($" writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + } + else + { + this.EmitFieldEncodeValue(sb, resolved, propName, fc, " ", false); + } + } + } + + private void EmitImplicitPrimitiveEncode(StringBuilder sb, Asn1Type type, string propName, FieldConfig fc, int tagNum, string indent, bool optional = false) + { + string tag = $"new Asn1Tag(TagClass.ContextSpecific, {tagNum})"; + string valueAccess = optional ? ".Value" : ""; + + if (type is Asn1OctetStringType) + { + sb.AppendLine($"{indent}writer.WriteOctetString({tag}, {propName}{valueAccess}.Span);"); + } + else if (type is Asn1IntegerType) + { + sb.AppendLine($"{indent}writer.WriteInteger({tag}, {propName}{valueAccess});"); + } + else if (type is Asn1BitStringType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($"{indent}writer.WriteBitString({tag}, {propName}.AsReadOnlySpan());"); + } + else + { + sb.AppendLine($"{indent}writer.WriteBitString({tag}, {propName}{valueAccess}.Span);"); + } + } + else + { + // fallback to explicit + sb.AppendLine($"{indent}writer.PushSequence({tag});"); + this.EmitFieldEncodeValue(sb, type, propName, fc, indent, false); + sb.AppendLine($"{indent}writer.PopSequence({tag});"); + } + } + + private void EmitFieldEncodeValue(StringBuilder sb, Asn1Type type, string propName, FieldConfig fc, string indent, bool optional) + { + // BackingType override — treat as referenced type for encode + if (!string.IsNullOrEmpty(fc.BackingType) && !fc.TreatAsEnum + && !fc.BackingType.EndsWith("[]") && !fc.BackingType.StartsWith("ReadOnlyMemory") + && fc.BackingType != "string" && fc.BackingType != "Oid" && fc.BackingType != "int" + && fc.BackingType != "int?" && fc.BackingType != "bool") + { + sb.AppendLine($"{indent}{propName}?.Encode(writer);"); + return; + } + + if (type is Asn1IntegerType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($"{indent}writer.WriteInteger((long){propName});"); + } + else + { + string valueAccess = optional ? ".Value" : ""; + sb.AppendLine($"{indent}writer.WriteInteger({propName}{valueAccess});"); + } + } + else if (type is Asn1OctetStringType) + { + sb.AppendLine(optional + ? $"{indent}writer.WriteOctetString({propName}.Value.Span);" + : $"{indent}writer.WriteOctetString({propName}.Span);"); + } + else if (type is Asn1BitStringType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($"{indent}writer.WriteBitString({propName}.AsReadOnlySpan());"); + } + else + { + string valueAccess = optional ? ".Value" : ""; + sb.AppendLine($"{indent}writer.WriteBitString({propName}{valueAccess}.Span);"); + } + } + else if (type is Asn1BooleanType) + { + sb.AppendLine($"{indent}writer.WriteBoolean({propName});"); + } + else if (type is Asn1NullType) + { + sb.AppendLine($"{indent}writer.WriteNull();"); + } + else if (type is Asn1ObjectIdentifierType) + { + sb.AppendLine($"{indent}writer.WriteObjectIdentifier({propName});"); + } + else if (type is Asn1EnumeratedType) + { + sb.AppendLine($"{indent}writer.WriteEnumeratedValue({propName});"); + } + else if (type is Asn1GeneralizedTimeType) + { + string valueAccess = optional ? ".Value" : ""; + sb.AppendLine($"{indent}writer.WriteGeneralizedTime({propName}{valueAccess});"); + } + else if (type is Asn1UtcTimeType) + { + string valueAccess = optional ? ".Value" : ""; + sb.AppendLine($"{indent}writer.WriteUtcTime({propName}{valueAccess});"); + } + else if (type is Asn1StringType strType) + { + string utn = GetUniversalTagNumber(strType.Kind); + sb.AppendLine($"{indent}writer.WriteCharacterString(UniversalTagNumber.{utn}, {propName});"); + } + else if (type is Asn1SequenceOfType seqOf) + { + sb.AppendLine($"{indent}writer.PushSequence();"); + sb.AppendLine($"{indent}"); + sb.AppendLine($"{indent}for (int i = 0; i < {propName}.Length; i++)"); + sb.AppendLine($"{indent}{{"); + this.EmitSequenceOfElementEncode(sb, seqOf.ElementType, propName, fc, indent + " "); + sb.AppendLine($"{indent}}}"); + sb.AppendLine(); + sb.AppendLine($"{indent}writer.PopSequence();"); + } + else if (type is Asn1ReferencedType) + { + sb.AppendLine($"{indent}{propName}?.Encode(writer);"); + } + else if (type is Asn1AnyType) + { + sb.AppendLine(optional + ? $"{indent}writer.WriteEncodedValue({propName}.Value.Span);" + : $"{indent}writer.WriteEncodedValue({propName}.Span);"); + } + } + + private void EmitSequenceOfElementEncode(StringBuilder sb, Asn1Type elementType, string propName, FieldConfig fc, string indent) + { + var resolved = this.ResolveFieldType(elementType); + + if (resolved is Asn1OctetStringType) + { + sb.AppendLine($"{indent}writer.WriteOctetString({propName}[i].Span); "); + } + else if (resolved is Asn1IntegerType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($"{indent}writer.WriteInteger((long){propName}[i]); "); + } + else + { + sb.AppendLine($"{indent}writer.WriteInteger({propName}[i]); "); + } + } + else if (resolved is Asn1StringType strType) + { + string utn = GetUniversalTagNumber(strType.Kind); + sb.AppendLine($"{indent}writer.WriteCharacterString(UniversalTagNumber.{utn}, {propName}[i]); "); + } + else if (resolved is Asn1ObjectIdentifierType) + { + sb.AppendLine($"{indent}writer.WriteObjectIdentifier({propName}[i]); "); + } + else if (resolved is Asn1ReferencedType || elementType is Asn1ReferencedType) + { + sb.AppendLine($"{indent}{propName}[i]?.Encode(writer); "); + } + else + { + sb.AppendLine($"{indent}{propName}[i]?.Encode(writer); "); + } + } + + #endregion + + #region Field Decoding + + private void EmitFieldDecode(StringBuilder sb, Asn1Field field, TypeConfig tc, string className, Asn1Module module) + { + var fc = this.GetFieldConfig(tc, field.Name); + var propName = fc.CSharpName ?? ToPascalCase(field.Name); + int tagNum = this.GetTagNumber(field); + var innerType = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(innerType); + bool hasTag = field.Type is Asn1TaggedType; + bool isImplicit = this.IsImplicitTag(field, module); + + if (field.Optional) + { + sb.AppendLine($" if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, {tagNum})))"); + sb.AppendLine(" {"); + + if (hasTag && isImplicit && this.IsPrimitiveType(resolved)) + { + this.EmitImplicitPrimitiveDecode(sb, resolved, propName, fc, tagNum, " "); + } + else if (hasTag) + { + sb.AppendLine($" explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum})); "); + sb.AppendLine(" "); + this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", true); + sb.AppendLine(" explicitReader.ThrowIfNotEmpty();"); + } + + sb.AppendLine(" }"); + sb.AppendLine(); + } + else + { + if (hasTag && isImplicit && this.IsPrimitiveType(resolved)) + { + this.EmitImplicitPrimitiveDecode(sb, resolved, propName, fc, tagNum, " "); + sb.AppendLine(); + } + else if (hasTag) + { + sb.AppendLine($" explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum}));"); + sb.AppendLine(); + this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", false); + sb.AppendLine(); + sb.AppendLine(" explicitReader.ThrowIfNotEmpty();"); + sb.AppendLine(); + } + else + { + this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", false, "sequenceReader"); + } + } + } + + private void EmitImplicitPrimitiveDecode(StringBuilder sb, Asn1Type type, string propName, FieldConfig fc, int tagNum, string indent) + { + string tag = $"new Asn1Tag(TagClass.ContextSpecific, {tagNum})"; + + if (type is Asn1OctetStringType) + { + sb.AppendLine($"{indent}if (sequenceReader.TryReadPrimitiveOctetStringBytes({tag}, out ReadOnlyMemory tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = sequenceReader.ReadOctetString({tag});"); + sb.AppendLine($"{indent}}}"); + } + else if (type is Asn1BitStringType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($"{indent}if (sequenceReader.TryReadPrimitiveBitStringValue({tag}, out _, out ReadOnlyMemory tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = ({fc.EnumType})tmp{propName}.AsLong();"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = ({fc.EnumType})sequenceReader.ReadBitString({tag}, out _).AsLong();"); + sb.AppendLine($"{indent}}}"); + } + else + { + sb.AppendLine($"{indent}if (sequenceReader.TryReadPrimitiveBitStringValue({tag}, out _, out ReadOnlyMemory tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = sequenceReader.ReadBitString({tag}, out _);"); + sb.AppendLine($"{indent}}}"); + } + } + else + { + // Fallback - use explicit wrapping decode + sb.AppendLine($"{indent}explicitReader = sequenceReader.ReadSequence({tag});"); + this.EmitFieldDecodeValue(sb, type, propName, fc, indent, false); + sb.AppendLine($"{indent}explicitReader.ThrowIfNotEmpty();"); + } + } + + private void EmitFieldDecodeValue(StringBuilder sb, Asn1Type type, string propName, FieldConfig fc, string indent, bool optional, string readerName = "explicitReader") + { + string reader = readerName; + + // BackingType override — treat as referenced type for decode + if (!string.IsNullOrEmpty(fc.BackingType) && !fc.TreatAsEnum + && !fc.BackingType.EndsWith("[]") && !fc.BackingType.StartsWith("ReadOnlyMemory") + && fc.BackingType != "string" && fc.BackingType != "Oid" && fc.BackingType != "int" + && fc.BackingType != "int?" && fc.BackingType != "bool") + { + string typeName = fc.BackingType; + sb.AppendLine($"{indent}{typeName}.Decode<{typeName}>({reader}, out {typeName} tmp{propName});"); + sb.AppendLine($"{indent}decoded.{propName} = tmp{propName};"); + return; + } + + if (type is Asn1IntegerType) + { + string csharpType = (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + ? fc.EnumType : "int"; + + if (optional) + { + sb.AppendLine($"{indent}if ({reader}.TryReadInt32(out {csharpType} tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} {reader}.ThrowIfNotEmpty();"); + sb.AppendLine($"{indent}}}"); + } + else + { + sb.AppendLine($"{indent}if (!{reader}.TryReadInt32(out {csharpType} tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} {reader}.ThrowIfNotEmpty();"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}"); + sb.AppendLine($"{indent}decoded.{propName} = tmp{propName};"); + } + } + else if (type is Asn1OctetStringType) + { + sb.AppendLine($"{indent}if ({reader}.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = {reader}.ReadOctetString();"); + sb.AppendLine($"{indent}}}"); + } + else if (type is Asn1BitStringType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + string enumType = fc.EnumType; + sb.AppendLine($"{indent}if ({reader}.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = ({enumType})tmp{propName}.AsLong();"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = ({enumType}){reader}.ReadBitString(out _).AsLong();"); + sb.AppendLine($"{indent}}}"); + } + else + { + sb.AppendLine($"{indent}if ({reader}.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmp{propName}))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} decoded.{propName} = {reader}.ReadBitString(out _);"); + sb.AppendLine($"{indent}}}"); + } + + sb.AppendLine(); + } + else if (type is Asn1BooleanType) + { + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadBoolean();"); + } + else if (type is Asn1NullType) + { + sb.AppendLine($"{indent}{reader}.ReadNull();"); + } + else if (type is Asn1ObjectIdentifierType) + { + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadObjectIdentifier();"); + } + else if (type is Asn1EnumeratedType) + { + string enumType = fc.EnumType ?? fc.BackingType ?? "int"; + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadEnumeratedValue<{enumType}>();"); + } + else if (type is Asn1GeneralizedTimeType) + { + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadGeneralizedTime();"); + } + else if (type is Asn1UtcTimeType) + { + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadUtcTime();"); + } + else if (type is Asn1StringType strType) + { + string utn = GetUniversalTagNumber(strType.Kind); + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadCharacterString(UniversalTagNumber.{utn});"); + } + else if (type is Asn1SequenceOfType seqOf) + { + string elementTypeName = this.GetElementTypeName(seqOf.ElementType, fc); + sb.AppendLine($"{indent}// Decode SEQUENCE OF for {propName}"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} collectionReader = {reader}.ReadSequence();"); + sb.AppendLine($"{indent} var tmpList = new List<{elementTypeName}>();"); + sb.AppendLine($"{indent} {elementTypeName} tmpItem;"); + sb.AppendLine(); + sb.AppendLine($"{indent} while (collectionReader.HasData)"); + sb.AppendLine($"{indent} {{"); + this.EmitSequenceOfElementDecode(sb, seqOf.ElementType, fc, "tmpItem", indent + " "); + sb.AppendLine($"{indent} tmpList.Add(tmpItem);"); + sb.AppendLine($"{indent} }}"); + sb.AppendLine(); + sb.AppendLine($"{indent} decoded.{propName} = tmpList.ToArray();"); + sb.AppendLine($"{indent}}}"); + } + else if (type is Asn1ReferencedType refType) + { + string typeName = this.ResolveCSharpTypeName(refType.ReferenceName); + sb.AppendLine($"{indent}{typeName}.Decode<{typeName}>({reader}, out {typeName} tmp{propName});"); + sb.AppendLine($"{indent}decoded.{propName} = tmp{propName};"); + } + else if (type is Asn1AnyType) + { + sb.AppendLine($"{indent}decoded.{propName} = {reader}.ReadEncodedValue();"); + } + } + + private void EmitSequenceOfElementDecode(StringBuilder sb, Asn1Type elementType, FieldConfig fc, string varName, string indent) + { + var resolved = this.ResolveFieldType(elementType); + + // Check resolved primitive types FIRST, before checking if elementType is a reference + if (resolved is Asn1OctetStringType) + { + sb.AppendLine($"{indent}if (collectionReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmp))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} {varName} = tmp;"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}else"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} {varName} = collectionReader.ReadOctetString();"); + sb.AppendLine($"{indent}}}"); + } + else if (resolved is Asn1IntegerType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($"{indent}if (!collectionReader.TryReadInt32<{fc.EnumType}>(out {fc.EnumType} tmp))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} collectionReader.ThrowIfNotEmpty();"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}"); + sb.AppendLine($"{indent}{varName} = tmp; "); + } + else + { + sb.AppendLine($"{indent}if (!collectionReader.TryReadInt32(out int tmp))"); + sb.AppendLine($"{indent}{{"); + sb.AppendLine($"{indent} collectionReader.ThrowIfNotEmpty();"); + sb.AppendLine($"{indent}}}"); + sb.AppendLine($"{indent}"); + sb.AppendLine($"{indent}{varName} = tmp; "); + } + } + else if (resolved is Asn1StringType strType) + { + string utn = GetUniversalTagNumber(strType.Kind); + sb.AppendLine($"{indent}{varName} = collectionReader.ReadCharacterString(UniversalTagNumber.{utn});"); + } + else if (resolved is Asn1ObjectIdentifierType) + { + sb.AppendLine($"{indent}{varName} = collectionReader.ReadObjectIdentifier();"); + } + else if (fc != null && !string.IsNullOrEmpty(fc.BackingType) && fc.BackingType.EndsWith("[]")) + { + // BackingType array override — use the specified element type instead of the ASN.1 type + string typeName = fc.BackingType.Substring(0, fc.BackingType.Length - 2); + sb.AppendLine($"{indent}{typeName}.Decode<{typeName}>(collectionReader, out {typeName} tmp);"); + sb.AppendLine($"{indent}{varName} = tmp; "); + } + else if (elementType is Asn1ReferencedType refType) + { + string typeName = this.ResolveCSharpTypeName(refType.ReferenceName); + sb.AppendLine($"{indent}{typeName}.Decode<{typeName}>(collectionReader, out {typeName} tmp);"); + sb.AppendLine($"{indent}{varName} = tmp; "); + } + else if (elementType is Asn1SequenceType) + { + // Inline sequence in SEQUENCE OF - decode as self type + sb.AppendLine($"{indent}// inline sequence element decode"); + } + } + + #endregion + + #region Helpers + + private TypeConfig GetTypeConfig(string name) + { + if (this.config.Types.TryGetValue(name, out var tc)) + { + return tc; + } + + return new TypeConfig { CSharpName = ToPascalCase(name) }; + } + + private FieldConfig GetFieldConfig(TypeConfig tc, string fieldName) + { + if (tc.Fields.TryGetValue(fieldName, out var fc)) + { + return fc; + } + + return new FieldConfig(); + } + + private string ResolveCSharpTypeName(string asnName) + { + if (this.config.Types.TryGetValue(asnName, out var tc) && tc.CSharpName != null) + { + return tc.CSharpName; + } + + return ToPascalCase(asnName); + } + + private string GetChoiceFieldCSharpType(Asn1Field field, FieldConfig fc) + { + // CHOICE fields are always nullable (only one is set at a time) + var baseType = this.GetCSharpType(field, fc); + + // Value types need ? suffix; reference types are already nullable + if (baseType == "int" || baseType == "bool" || baseType == "DateTimeOffset" + || baseType == "ReadOnlyMemory") + { + return baseType + "?"; + } + + return baseType; + } + + private string GetCSharpType(Asn1Field field, FieldConfig fc) + { + // BackingType override takes precedence + if (!string.IsNullOrEmpty(fc.BackingType)) + { + return fc.BackingType; + } + + var type = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(type); + bool optional = field.Optional; + + return this.GetCSharpTypeForAsn1Type(resolved, fc, optional); + } + + private string GetCSharpTypeForAsn1Type(Asn1Type type, FieldConfig fc, bool optional) + { + if (type is Asn1IntegerType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + return fc.EnumType; + } + + return optional ? "int?" : "int"; + } + + if (type is Asn1BooleanType) + { + return optional ? "bool?" : "bool"; + } + + if (type is Asn1OctetStringType) + { + return optional ? "ReadOnlyMemory?" : "ReadOnlyMemory"; + } + + if (type is Asn1BitStringType) + { + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + return fc.EnumType; + } + + return optional ? "ReadOnlyMemory?" : "ReadOnlyMemory"; + } + + if (type is Asn1NullType) + { + return "ReadOnlyMemory?"; + } + + if (type is Asn1ObjectIdentifierType) + { + return "Oid"; + } + + if (type is Asn1EnumeratedType) + { + return fc.EnumType ?? fc.BackingType ?? "int"; + } + + if (type is Asn1GeneralizedTimeType || type is Asn1UtcTimeType) + { + return optional ? "DateTimeOffset?" : "DateTimeOffset"; + } + + if (type is Asn1StringType) + { + return "string"; + } + + if (type is Asn1SequenceOfType seqOf) + { + string elemType = this.GetElementTypeName(seqOf.ElementType, fc); + return $"{elemType}[]"; + } + + if (type is Asn1SetOfType setOf) + { + string elemType = this.GetElementTypeName(setOf.ElementType, fc); + return $"{elemType}[]"; + } + + if (type is Asn1ReferencedType refType) + { + return this.ResolveCSharpTypeName(refType.ReferenceName); + } + + if (type is Asn1AnyType) + { + return optional ? "ReadOnlyMemory?" : "ReadOnlyMemory"; + } + + return "object"; + } + + private string GetElementTypeName(Asn1Type elementType, FieldConfig fc = null) + { + // If BackingType is an array like "KrbAuthorizationData[]", extract the element type + if (fc != null && !string.IsNullOrEmpty(fc.BackingType) && fc.BackingType.EndsWith("[]")) + { + return fc.BackingType.Substring(0, fc.BackingType.Length - 2); + } + + // Resolve through aliases first + var resolved = this.ResolveFieldType(elementType); + + if (resolved is Asn1OctetStringType) + { + return "ReadOnlyMemory"; + } + + if (resolved is Asn1IntegerType) + { + if (fc != null && fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + return fc.EnumType; + } + + return "int"; + } + + if (resolved is Asn1StringType) + { + return "string"; + } + + if (resolved is Asn1ObjectIdentifierType) + { + return "Oid"; + } + + if (elementType is Asn1ReferencedType refType) + { + return this.ResolveCSharpTypeName(refType.ReferenceName); + } + + return "object"; + } + + private string GetReferencedTypeName(Asn1Field field) + { + var inner = this.UnwrapTaggedType(field.Type); + + if (inner is Asn1ReferencedType refType) + { + return this.ResolveCSharpTypeName(refType.ReferenceName); + } + + return ToPascalCase(field.Name); + } + + private Asn1Type UnwrapTaggedType(Asn1Type type) + { + while (type is Asn1TaggedType tagged) + { + type = tagged.InnerType; + } + + return type; + } + + private int GetTagNumber(Asn1Field field) + { + if (field.Type is Asn1TaggedType tagged) + { + return tagged.TagNumber; + } + + return 0; + } + + private bool IsSequenceOfField(Asn1Field field) + { + var inner = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(inner); + return resolved is Asn1SequenceOfType || inner is Asn1SequenceOfType; + } + + private bool IsSetOfField(Asn1Field field) + { + var inner = this.UnwrapTaggedType(field.Type); + var resolved = this.ResolveFieldType(inner); + return resolved is Asn1SetOfType || inner is Asn1SetOfType; + } + + private bool IsPrimitiveType(Asn1Type type) + { + return type is Asn1OctetStringType || + type is Asn1IntegerType || + type is Asn1BitStringType || + type is Asn1BooleanType || + type is Asn1NullType || + type is Asn1ObjectIdentifierType || + type is Asn1GeneralizedTimeType || + type is Asn1UtcTimeType || + type is Asn1StringType; + } + + private bool NeedsCollections(Asn1Type type) + { + if (type is Asn1SequenceType seq) + { + return seq.Fields.Any(f => + { + var inner = this.UnwrapTaggedType(f.Type); + var resolved = this.ResolveFieldType(inner); + return resolved is Asn1SequenceOfType || inner is Asn1SequenceOfType || + resolved is Asn1SetOfType || inner is Asn1SetOfType; + }); + } + + return false; + } + + private static string GetUniversalTagNumber(Asn1StringKind kind) + { + return kind switch + { + Asn1StringKind.UTF8String => "UTF8String", + Asn1StringKind.PrintableString => "PrintableString", + Asn1StringKind.IA5String => "IA5String", + Asn1StringKind.VisibleString => "VisibleString", + Asn1StringKind.GeneralString => "GeneralString", + Asn1StringKind.BMPString => "BMPString", + Asn1StringKind.T61String => "T61String", + _ => "UTF8String", + }; + } + + public static string ToPascalCase(string name) + { + if (string.IsNullOrEmpty(name)) + { + return name; + } + + var parts = name.Split('-', '_'); + var sb = new StringBuilder(); + + foreach (var part in parts) + { + if (part.Length > 0) + { + sb.Append(char.ToUpperInvariant(part[0])); + sb.Append(part.Substring(1)); + } + } + + return sb.ToString(); + } + + #endregion + } +} diff --git a/Tools/Asn1CodeGen/Program.cs b/Tools/Asn1CodeGen/Program.cs new file mode 100644 index 00000000..5783f5fa --- /dev/null +++ b/Tools/Asn1CodeGen/Program.cs @@ -0,0 +1,130 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +namespace Kerberos.NET.Asn1CodeGen +{ + internal class Program + { + private static int Main(string[] args) + { + if (args.Length < 1) + { + Console.Error.WriteLine("Usage: Asn1CodeGen [--output ] [--config ]"); + Console.Error.WriteLine(" Asn1CodeGen [--output ] [--config ]"); + return 1; + } + + string input = args[0]; + string outputDir = null; + string configPath = null; + + for (int i = 1; i < args.Length; i++) + { + switch (args[i]) + { + case "--output" when i + 1 < args.Length: + outputDir = args[++i]; + break; + case "--config" when i + 1 < args.Length: + configPath = args[++i]; + break; + } + } + + var config = LoadConfig(configPath); + + var files = new List(); + + if (Directory.Exists(input)) + { + files.AddRange(Directory.GetFiles(input, "*.asn", SearchOption.AllDirectories)); + } + else if (File.Exists(input)) + { + files.Add(input); + } + else + { + Console.Error.WriteLine($"Input not found: {input}"); + return 1; + } + + if (files.Count == 0) + { + Console.Error.WriteLine("No .asn files found."); + return 1; + } + + int totalGenerated = 0; + + foreach (var file in files) + { + try + { + string source = File.ReadAllText(file); + var lexer = new Asn1Lexer(source); + var tokens = lexer.Tokenize(); + var parser = new Asn1Parser(tokens); + var modules = parser.ParseAll(); + + var emitter = new CSharpCodeEmitter(modules, config); + var generated = emitter.EmitAll(); + + string outDir = outputDir ?? Path.GetDirectoryName(file); + + foreach (var kvp in generated) + { + string outPath = Path.Combine(outDir, kvp.Key); + string existing = File.Exists(outPath) ? File.ReadAllText(outPath) : null; + + if (existing != kvp.Value) + { + File.WriteAllText(outPath, kvp.Value); + Console.WriteLine($" Generated: {kvp.Key}"); + } + else + { + Console.WriteLine($" Unchanged: {kvp.Key}"); + } + + totalGenerated++; + } + } + catch (Asn1ParseException ex) + { + Console.Error.WriteLine($"Parse error in {file}: {ex.Message}"); + return 2; + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error processing {file}: {ex.Message}"); + return 3; + } + } + + Console.WriteLine($"Done. {totalGenerated} file(s) processed."); + return 0; + } + + private static EmitterConfig LoadConfig(string path) + { + if (string.IsNullOrEmpty(path) || !File.Exists(path)) + { + return new EmitterConfig(); + } + + var json = File.ReadAllText(path); + return JsonSerializer.Deserialize(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + }) ?? new EmitterConfig(); + } + } +} diff --git a/Tools/Asn1CodeGen/testdata/KerberosV5.asn b/Tools/Asn1CodeGen/testdata/KerberosV5.asn new file mode 100644 index 00000000..ca725eba --- /dev/null +++ b/Tools/Asn1CodeGen/testdata/KerberosV5.asn @@ -0,0 +1,28 @@ +KerberosV5 DEFINITIONS EXPLICIT TAGS ::= BEGIN + +-- Checksum type +Checksum ::= SEQUENCE { + cksumtype [0] INTEGER, + checksum [1] OCTET STRING +} + +-- Authenticator +Authenticator ::= [APPLICATION 2] SEQUENCE { + authenticator-vno [0] INTEGER, + crealm [1] GeneralString, + cname [2] PrincipalName, + cksum [3] Checksum OPTIONAL, + cusec [4] INTEGER, + ctime [5] GeneralizedTime, + subkey [6] EncryptionKey OPTIONAL, + seq-number [7] INTEGER OPTIONAL, + authorization-data [8] SEQUENCE OF AuthorizationData OPTIONAL +} + +-- Simple choice +NegotiationToken ::= CHOICE { + negTokenInit [0] NegTokenInit, + negTokenResp [1] NegTokenResp +} + +END diff --git a/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs b/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs new file mode 100644 index 00000000..2db792ea --- /dev/null +++ b/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs @@ -0,0 +1,257 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Kerberos.NET.Asn1CodeGen +{ + [Generator(LanguageNames.CSharp)] + public class Asn1IncrementalGenerator : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Look for .asn files added as AdditionalFiles + var asnFiles = context.AdditionalTextsProvider + .Where(static file => file.Path.EndsWith(".asn", StringComparison.OrdinalIgnoreCase)); + + // Also look for .asn.json config files + var configFiles = context.AdditionalTextsProvider + .Where(static file => file.Path.EndsWith(".asn.json", StringComparison.OrdinalIgnoreCase)); + + // Combine asn files with config + var asnWithConfig = asnFiles.Collect().Combine(configFiles.Collect()); + + context.RegisterSourceOutput(asnWithConfig, static (spc, source) => + { + var (asnTexts, configTexts) = source; + + // Load config from .asn.json files + var config = new EmitterConfig(); + + foreach (var configText in configTexts) + { + var configContent = configText.GetText(spc.CancellationToken); + + if (configContent != null) + { + MergeSimpleJsonConfig(config, configContent.ToString()); + } + } + + foreach (var asnText in asnTexts) + { + spc.CancellationToken.ThrowIfCancellationRequested(); + + var text = asnText.GetText(spc.CancellationToken); + + if (text == null) + { + continue; + } + + try + { + var source_text = text.ToString(); + var lexer = new Asn1Lexer(source_text); + var tokens = lexer.Tokenize(); + var parser = new Asn1Parser(tokens); + var modules = parser.ParseAll(); + + var emitter = new CSharpCodeEmitter(modules, config); + var generated = emitter.EmitAll(); + + foreach (var kvp in generated) + { + string hintName = kvp.Key.Replace(".generated.cs", ".g.cs"); + spc.AddSource(hintName, SourceText.From(kvp.Value, Encoding.UTF8)); + } + } + catch (Asn1ParseException ex) + { + spc.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor( + "ASN0001", + "ASN.1 Parse Error", + "Error parsing {0}: {1}", + "Asn1CodeGen", + DiagnosticSeverity.Error, + isEnabledByDefault: true), + Location.None, + Path.GetFileName(asnText.Path), + ex.Message)); + } + catch (Exception ex) + { + spc.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor( + "ASN0002", + "ASN.1 Code Generation Error", + "Error generating code from {0}: {1}", + "Asn1CodeGen", + DiagnosticSeverity.Error, + isEnabledByDefault: true), + Location.None, + Path.GetFileName(asnText.Path), + ex.Message)); + } + } + }); + } + + /// + /// Minimal JSON config parser for netstandard2.0 (no System.Text.Json). + /// Parses the EmitterConfig format with Types, TypeConfig, FieldConfig. + /// + private static void MergeSimpleJsonConfig(EmitterConfig config, string json) + { + // Strip comments and normalize whitespace + json = Regex.Replace(json, @"//[^\n]*", ""); + json = json.Trim(); + + // Find "Types" object + int typesStart = json.IndexOf("\"Types\""); + if (typesStart < 0) return; + + int typesObjStart = json.IndexOf('{', typesStart + 7); + if (typesObjStart < 0) return; + + string typesContent = ExtractBalancedBraces(json, typesObjStart); + if (typesContent == null) return; + + // Parse each type entry: "TypeName": { ... } + int pos = 0; + while (pos < typesContent.Length) + { + int keyStart = typesContent.IndexOf('"', pos); + if (keyStart < 0) break; + + int keyEnd = typesContent.IndexOf('"', keyStart + 1); + if (keyEnd < 0) break; + + string typeName = typesContent.Substring(keyStart + 1, keyEnd - keyStart - 1); + + int objStart = typesContent.IndexOf('{', keyEnd); + if (objStart < 0) break; + + string typeObj = ExtractBalancedBraces(typesContent, objStart); + if (typeObj == null) break; + + var tc = ParseTypeConfig(typeObj); + config.Types[typeName] = tc; + + pos = objStart + typeObj.Length + 2; // past closing brace + } + } + + private static TypeConfig ParseTypeConfig(string json) + { + var tc = new TypeConfig(); + + tc.CSharpName = ExtractStringValue(json, "CSharpName"); + tc.InheritsFrom = ExtractStringValue(json, "InheritsFrom"); + + string ns = ExtractStringValue(json, "Namespace"); + if (ns != null) tc.Namespace = ns; + + // Parse Fields object + int fieldsStart = json.IndexOf("\"Fields\""); + if (fieldsStart >= 0) + { + int fieldsObjStart = json.IndexOf('{', fieldsStart + 8); + if (fieldsObjStart >= 0) + { + string fieldsContent = ExtractBalancedBraces(json, fieldsObjStart); + if (fieldsContent != null) + { + ParseFieldConfigs(fieldsContent, tc.Fields); + } + } + } + + return tc; + } + + private static void ParseFieldConfigs(string json, Dictionary fields) + { + int pos = 0; + while (pos < json.Length) + { + int keyStart = json.IndexOf('"', pos); + if (keyStart < 0) break; + + int keyEnd = json.IndexOf('"', keyStart + 1); + if (keyEnd < 0) break; + + string fieldName = json.Substring(keyStart + 1, keyEnd - keyStart - 1); + + int objStart = json.IndexOf('{', keyEnd); + if (objStart < 0) break; + + string fieldObj = ExtractBalancedBraces(json, objStart); + if (fieldObj == null) break; + + var fc = new FieldConfig + { + CSharpName = ExtractStringValue(fieldObj, "CSharpName"), + BackingType = ExtractStringValue(fieldObj, "BackingType"), + EnumType = ExtractStringValue(fieldObj, "EnumType"), + TreatAsEnum = ExtractBoolValue(fieldObj, "TreatAsEnum") + }; + + fields[fieldName] = fc; + pos = objStart + fieldObj.Length + 2; + } + } + + private static string ExtractStringValue(string json, string key) + { + string pattern = "\"" + key + "\"\\s*:\\s*\"([^\"]*?)\""; + var match = Regex.Match(json, pattern); + return match.Success ? match.Groups[1].Value : null; + } + + private static bool ExtractBoolValue(string json, string key) + { + string pattern = "\"" + key + "\"\\s*:\\s*(true|false)"; + var match = Regex.Match(json, pattern, RegexOptions.IgnoreCase); + return match.Success && match.Groups[1].Value.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + private static string ExtractBalancedBraces(string text, int start) + { + if (start >= text.Length || text[start] != '{') return null; + + int depth = 0; + bool inString = false; + + for (int i = start; i < text.Length; i++) + { + char c = text[i]; + + if (inString) + { + if (c == '\\') { i++; continue; } + if (c == '"') inString = false; + continue; + } + + if (c == '"') { inString = true; continue; } + if (c == '{') depth++; + if (c == '}') { depth--; if (depth == 0) return text.Substring(start + 1, i - start - 1); } + } + + return null; + } + } +} diff --git a/Tools/Asn1SourceGenerator/Asn1SourceGenerator.csproj b/Tools/Asn1SourceGenerator/Asn1SourceGenerator.csproj new file mode 100644 index 00000000..6388be67 --- /dev/null +++ b/Tools/Asn1SourceGenerator/Asn1SourceGenerator.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + 12 + Kerberos.NET.Asn1CodeGen + false + false + true + RS1035 + true + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + From 2a0aced42b552f8af958cc54c60652918920e0a6 Mon Sep 17 00:00:00 2001 From: Steve Syfuhs Date: Thu, 26 Mar 2026 11:45:29 -0700 Subject: [PATCH 2/4] Convert the rest of the ASN --- Kerberos.NET/Asn1/AsnXml.targets | 48 - Kerberos.NET/Asn1/asn.xsd | 244 ---- Kerberos.NET/Asn1/asn.xslt | 1184 ----------------- .../KrbChangePasswdData.generated.cs | 179 --- .../ChangePassword/KrbChangePasswdData.xml | 18 - .../Entities/Kkdcp/KdcProxyMessage.cs | 2 +- .../Kkdcp/KdcProxyMessage.generated.cs | 187 --- .../Entities/Kkdcp/KdcProxyMessage.xml | 17 - .../Entities/Krb/IAKerbHeader.generated.cs | 195 --- Kerberos.NET/Entities/Krb/IAKerbHeader.xml | 27 - Kerberos.NET/Entities/Krb/KrbApRep.xml | 24 - Kerberos.NET/Entities/Krb/KrbApReq.xml | 21 - Kerberos.NET/Entities/Krb/KrbAsRep.xml | 11 - Kerberos.NET/Entities/Krb/KrbAsReq.xml | 11 - .../Entities/Krb/KrbAuthenticator.xml | 32 - .../Entities/Krb/KrbAuthorizationData.xml | 15 - .../KrbAuthorizationDataSequence.generated.cs | 151 --- .../Krb/KrbAuthorizationDataSequence.xml | 16 - Kerberos.NET/Entities/Krb/KrbChecksum.xml | 15 - Kerberos.NET/Entities/Krb/KrbCred.xml | 21 - Kerberos.NET/Entities/Krb/KrbCredInfo.xml | 35 - .../Entities/Krb/KrbETypeInfo2.generated.cs | 148 --- Kerberos.NET/Entities/Krb/KrbETypeInfo2.xml | 13 - .../Entities/Krb/KrbETypeInfo2Entry.xml | 19 - .../Entities/Krb/KrbETypeList.generated.cs | 157 --- Kerberos.NET/Entities/Krb/KrbETypeList.xml | 17 - Kerberos.NET/Entities/Krb/KrbEncApRepPart.xml | 19 - Kerberos.NET/Entities/Krb/KrbEncAsRepPart.xml | 12 - .../Entities/Krb/KrbEncKdcRepPart.xml | 42 - .../Entities/Krb/KrbEncKrbCredPart.xml | 27 - .../Entities/Krb/KrbEncKrbPrivPart.xml | 24 - .../Entities/Krb/KrbEncTgsRepPart.xml | 12 - .../Entities/Krb/KrbEncTicketPart.xml | 37 - .../Entities/Krb/KrbEncryptedData.xml | 17 - .../Entities/Krb/KrbEncryptionKey.xml | 15 - Kerberos.NET/Entities/Krb/KrbError.xml | 38 - .../Entities/Krb/KrbErrorData.generated.cs | 158 --- Kerberos.NET/Entities/Krb/KrbErrorData.xml | 14 - Kerberos.NET/Entities/Krb/KrbHostAddress.xml | 18 - Kerberos.NET/Entities/Krb/KrbKdcRep.xml | 31 - Kerberos.NET/Entities/Krb/KrbKdcReq.xml | 21 - Kerberos.NET/Entities/Krb/KrbKdcReqBody.xml | 41 - Kerberos.NET/Entities/Krb/KrbLastReq.xml | 15 - .../Entities/Krb/KrbMethodData.generated.cs | 148 --- Kerberos.NET/Entities/Krb/KrbMethodData.xml | 13 - Kerberos.NET/Entities/Krb/KrbPaData.xml | 15 - Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.xml | 15 - .../Entities/Krb/KrbPaForUser.generated.cs | 168 --- Kerberos.NET/Entities/Krb/KrbPaForUser.xml | 19 - .../Entities/Krb/KrbPaPacOptions.generated.cs | 146 -- Kerberos.NET/Entities/Krb/KrbPaPacOptions.xml | 18 - .../Entities/Krb/KrbPaPacRequest.generated.cs | 134 -- Kerberos.NET/Entities/Krb/KrbPaPacRequest.xml | 14 - .../Krb/KrbPaS4uX509User.generated.cs | 159 --- .../Entities/Krb/KrbPaS4uX509User.xml | 28 - .../Krb/KrbPaSvrReferralData.generated.cs | 154 --- .../Entities/Krb/KrbPaSvrReferralData.xml | 17 - .../Entities/Krb/KrbPrincipalName.xml | 18 - Kerberos.NET/Entities/Krb/KrbPriv.xml | 19 - .../Entities/Krb/KrbS4uUserId.generated.cs | 224 ---- Kerberos.NET/Entities/Krb/KrbS4uUserId.xml | 31 - Kerberos.NET/Entities/Krb/KrbTgsRep.xml | 11 - Kerberos.NET/Entities/Krb/KrbTgsReq.xml | 11 - Kerberos.NET/Entities/Krb/KrbTicket.xml | 19 - .../Entities/Krb/KrbTransitedEncoding.xml | 15 - .../KrbAlgorithmIdentifier.generated.cs | 140 -- .../Pkinit/KrbAlgorithmIdentifier.xml | 15 - Kerberos.NET/Entities/Pkinit/KrbAuthPack.xml | 43 - .../Entities/Pkinit/KrbDHReplyInfo.xml | 28 - ...DiffieHellmanDomainParameters.generated.cs | 165 --- .../KrbDiffieHellmanDomainParameters.xml | 21 - ...ieHellmanValidationParameters.generated.cs | 140 -- .../KrbDiffieHellmanValidationParameters.xml | 15 - .../Pkinit/KrbExternalPrincipalIdentifier.xml | 39 - .../Entities/Pkinit/KrbKdcDHKeyInfo.xml | 30 - .../Entities/Pkinit/KrbPKAuthenticator.xml | 28 - Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.xml | 32 - Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.xml | 46 - .../KrbSubjectPublicKeyInfo.generated.cs | 141 -- .../Pkinit/KrbSubjectPublicKeyInfo.xml | 15 - .../KrbFastArmor.generated.cs | 168 --- .../PreAuthentication/KrbFastArmor.xml | 18 - .../KrbFastArmoredRep.generated.cs | 137 -- .../PreAuthentication/KrbFastArmoredRep.xml | 16 - .../KrbFastArmoredReq.generated.cs | 179 --- .../PreAuthentication/KrbFastArmoredReq.xml | 31 - .../KrbFastFinished.generated.cs | 193 --- .../PreAuthentication/KrbFastFinished.xml | 29 - .../PreAuthentication/KrbFastReq.generated.cs | 195 --- .../Entities/PreAuthentication/KrbFastReq.xml | 26 - .../KrbFastResponse.generated.cs | 219 --- .../PreAuthentication/KrbFastResponse.xml | 28 - .../KrbPaAuthenticationSet.generated.cs | 156 --- .../KrbPaAuthenticationSet.xml | 22 - ...KrbPaAuthenticationSetElement.generated.cs | 195 --- .../KrbPaAuthenticationSetElement.xml | 22 - .../KrbPaFxFastReply.generated.cs | 132 -- .../PreAuthentication/KrbPaFxFastReply.xml | 14 - .../KrbPaFxFastRequest.generated.cs | 132 -- .../PreAuthentication/KrbPaFxFastRequest.xml | 13 - Kerberos.NET/Entities/SpNego/NegTokenInit.xml | 23 - Kerberos.NET/Entities/SpNego/NegTokenResp.xml | 25 - .../Entities/SpNego/NegotiationToken.xml | 15 - Kerberos.NET/Kerberos.NET.csproj | 32 - Kerberos.NET/kerberos.asn | 192 +++ Kerberos.NET/kerberos.asn.json | 346 +++-- Tools/Asn1CodeGen/CSharpCodeEmitter.cs | 478 ++++++- .../Asn1IncrementalGenerator.cs | 59 +- version.json | 2 +- 109 files changed, 912 insertions(+), 7827 deletions(-) delete mode 100644 Kerberos.NET/Asn1/AsnXml.targets delete mode 100644 Kerberos.NET/Asn1/asn.xsd delete mode 100644 Kerberos.NET/Asn1/asn.xslt delete mode 100644 Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.generated.cs delete mode 100644 Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.xml delete mode 100644 Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.generated.cs delete mode 100644 Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.xml delete mode 100644 Kerberos.NET/Entities/Krb/IAKerbHeader.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/IAKerbHeader.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbApRep.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbApReq.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbAsRep.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbAsReq.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbAuthenticator.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbAuthorizationData.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbChecksum.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbCred.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbCredInfo.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbETypeInfo2.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbETypeInfo2.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbETypeList.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbETypeList.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncApRepPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncAsRepPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncTicketPart.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncryptedData.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbEncryptionKey.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbError.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbErrorData.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbErrorData.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbHostAddress.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbKdcRep.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbKdcReq.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbKdcReqBody.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbLastReq.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbMethodData.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbMethodData.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaData.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaForUser.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaForUser.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaPacOptions.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaPacOptions.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaPacRequest.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaPacRequest.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaS4uX509User.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaS4uX509User.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPrincipalName.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbPriv.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbS4uUserId.generated.cs delete mode 100644 Kerberos.NET/Entities/Krb/KrbS4uUserId.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbTgsRep.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbTgsReq.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbTicket.xml delete mode 100644 Kerberos.NET/Entities/Krb/KrbTransitedEncoding.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbAuthPack.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.xml delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.generated.cs delete mode 100644 Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastReq.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastReq.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.xml delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.generated.cs delete mode 100644 Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.xml delete mode 100644 Kerberos.NET/Entities/SpNego/NegTokenInit.xml delete mode 100644 Kerberos.NET/Entities/SpNego/NegTokenResp.xml delete mode 100644 Kerberos.NET/Entities/SpNego/NegotiationToken.xml diff --git a/Kerberos.NET/Asn1/AsnXml.targets b/Kerberos.NET/Asn1/AsnXml.targets deleted file mode 100644 index ef5f99b2..00000000 --- a/Kerberos.NET/Asn1/AsnXml.targets +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - Asn1\asn.xsd - - - - - - - - - - - - - - - - - - - - - - diff --git a/Kerberos.NET/Asn1/asn.xsd b/Kerberos.NET/Asn1/asn.xsd deleted file mode 100644 index 82d9cb22..00000000 --- a/Kerberos.NET/Asn1/asn.xsd +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Asn1/asn.xslt b/Kerberos.NET/Asn1/asn.xslt deleted file mode 100644 index 3b95f841..00000000 --- a/Kerberos.NET/Asn1/asn.xslt +++ /dev/null @@ -1,1184 +0,0 @@ - - - - - - - - - Error, unknown node "" - - - - Error, unknown FieldDef node [] - - - - Error, unknown CollectionElementType node () [] - - - - Error, unknown EncodeOptional node [] - - - - Error, unknown EncodeSimpleValue node [] - - - - Error, unknown DefaultTag node [] - - - - Error, unknown DecodeSimpleValue node [] - - - - - - - // ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography.Asn1; - -namespace -{ - public partial class : - { - /* - */ - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, ); - - public override ReadOnlyMemory<byte> EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static DecodeApplication(ReadOnlyMemory<byte> encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - } -} - - - // ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace -{ - public partial class - { - /* - */ - - -#if DEBUG - static () - { - decoded = new (); - - AsnReader reader; - AsnReader collectionReader; - } -#endif // Encoding methods - public ReadOnlyMemory<byte> Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - EncodeApplication(writer, ApplicationTag);Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory<byte> EncodeApplication() => new ReadOnlyMemory<byte>(); - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, ); - - public virtual ReadOnlyMemory<byte> EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static DecodeApplication(ReadOnlyMemory<byte> encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - decoded; - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal static DecodeApplication<T>(AsnReader reader, out T decoded) - where T: , new() - { - var sequence = reader.ReadSequence(ApplicationTag); - - Decode(sequence, Asn1Tag.Sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory<byte> EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static Decode(ReadOnlyMemory<byte> data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static Decode(Asn1Tag expectedTag, ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode<T>(AsnReader reader, out T decoded) - where T: , new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - DecodeApplication(reader, out decoded);Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode<T>(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: , new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader defaultReader; - AsnReader collectionReader; - - sequenceReader.ThrowIfNotEmpty(); - } - } -} - - - // ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace -{ - public partial class - { - /* - */ - -#if DEBUG - static () - { - var usedTags = new System.Collections.Generic.Dictionary<Asn1Tag, string>(); - Action<Asn1Tag, string> ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - } -#endif - // Encoding methods - public ReadOnlyMemory<byte> Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - private static readonly Asn1Tag ApplicationTag = new Asn1Tag(TagClass.Application, ); - - public virtual ReadOnlyMemory<byte> EncodeApplication() - { - return EncodeApplication(ApplicationTag); - } - - public static DecodeApplication(ReadOnlyMemory<byte> encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - var sequence = reader.ReadSequence(ApplicationTag); - - decoded; - Decode(sequence, out decoded); - sequence.ThrowIfNotEmpty(); - - reader.ThrowIfNotEmpty(); - - return decoded; - } - - internal ReadOnlyMemory<byte> EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static Decode(ReadOnlyMemory<byte> data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static Decode(ReadOnlyMemory<byte> encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode<T>(AsnReader reader, out T decoded) - where T: , new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader explicitReader; - AsnReader collectionReader; - - else - { - throw new CryptographicException(); - } - } - } -} - - - - Error: defaultDerInit and optional both specified in [] - - - - Error: implicitTag and explicitTag both specified in [] - - - - Error: implicitTag and universalTagNumber both specified in [] - - - - Error: neiher optional or defaultDerInit may be specified for fields in a Choice type () - - - - private static byte[] = { }; - - - - - reader = new AsnReader(, AsnEncodingRules.DER); - reader.ThrowIfNotEmpty(); - - - - - ensureUniqueTag(new Asn1Tag((UniversalTagNumber)), ""); - - ensureUniqueTag(, ""); - - - - - - - - // DEFAULT value handler for . - { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - - - - ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual()) - { - writer.WriteEncodedValue(encoded.ToArray()); - } - } - } - - - - - - - - - - - - - - if (Asn1Extension.HasValue()) - { - } - - - - - if (Asn1Extension.HasValue()) - { - } - - - - - if (Asn1Extension.HasValue()) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - wroteValue = true; - } - - - - if ( != null) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - wroteValue = true; - } - - - - - - - - - // DEFAULT value handler for . - { - using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) - { - - - - ReadOnlySpan<byte> encoded = tmp.EncodeAsSpan(); - - if (!encoded.SequenceEqual()) - { - writer.PushSequence(); - writer.WriteEncodedValue(encoded.ToArray()); - writer.PopSequence(); - } - } - } - - - writer.PushSequence(); - writer.PopSequence(); - - - - - - - - - - - - - - - else if (tag.HasSameClassAndValue()) - { - explicitReader = reader.ReadSequence(); - explicitReader.ThrowIfNotEmpty(); - } - - else if (tag.HasSameClassAndValue()) - { - } - - - - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue()) - { - explicitReader = sequenceReader.ReadSequence(); - - explicitReader.ThrowIfNotEmpty(); - } - - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue()) - { - } - - - - if (sequenceReader.HasData) - { - } - - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue()) - { - } - - - - - explicitReader = sequenceReader.ReadSequence(); - - explicitReader.ThrowIfNotEmpty(); - - - - - - - - - - - - - - - - - public { get; set; } - - - - - - - - - - - ?.Encode(); - - - - - - - - - .Decode<>(, out tmp); - = tmp; - - .Decode<>(, out tmp); - = tmp; - - - - Asn1Tag.Sequence - - - public ReadOnlyMemory<byte>? { get; set; } - - - ReadOnlyMemory<byte> - - - - - - - - // Validator for tag constraint for - { - if (!Asn1Tag.TryDecode(?.Span, out Asn1Tag validateTag, out _) || - !validateTag.HasSameClassAndValue()) - { - throw new CryptographicException(); - } - } - - - .WriteEncodedValue(.Value.Span); - - - - - - - - - = .ReadEncodedValue(); - - if (!.PeekTag().HasSameClassAndValue()) - { - throw new CryptographicException(); - } - - = .ReadEncodedValue(); - - = .ReadEncodedValue(); - - - - new Asn1Tag([0]) - - new Asn1Tag((UniversalTagNumber)) - - - public bool? { get; set; } - - - bool - - - - - - - - .WriteBoolean(); - - - - - - - - = .ReadBoolean(); - - - Asn1Tag.Boolean - - - public System.Numerics.BigInteger? { get; set; } - - - - public ReadOnlyMemory<byte>? { get; set; } - - - - public byte? { get; set; } - - - - public int? { get; set; } - - - - public ? { get; set; } - - - System.Numerics.BigInteger - ReadOnlyMemory<byte> - byte - int - - - - - - - - - - .WriteInteger(.Value); - - - - - - - - - .WriteInteger((long).Value); - - - - - - - - - .WriteInteger(.Value.Span); - - - - - - - - = .ReadInteger(); - - - - - - - - = .ReadIntegerBytes(); - - - - - - - - - - if (.TryReadUInt8(out byte tmp)) - { - = tmp; - } - else - { - .ThrowIfNotEmpty(); - } - - - - if (!.TryReadUInt8(out )) - { - .ThrowIfNotEmpty(); - } - - - - - - - - - - - if (.TryReadInt32(out int tmp)) - { - = tmp; - } - else - { - .ThrowIfNotEmpty(); - } - - - - if (!.TryReadInt32(out int tmp)) - { - .ThrowIfNotEmpty(); - } - - = tmp; - - - - - - - - - - - - if (.TryReadInt32(out int tmp)) - { - = ()tmp; - } - else - { - .ThrowIfNotEmpty(); - } - - - - if (!.TryReadInt32(out tmp)) - { - .ThrowIfNotEmpty(); - } - - = tmp; - - - - Asn1Tag.Integer - - - public ? { get; set; } public ReadOnlyMemory<byte>? { get; set; } - - - ReadOnlyMemory<byte> - - - - - - - - .WriteBitString(.Value.Span.AsReadOnlySpan()); - - - - - - - - - if (.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory<byte> tmp)) - { - = ()tmp.AsLong(); - } - else - { - = ().ReadBitString(out _).AsLong(); - } - - - - Asn1Tag.PrimitiveBitString - - - public { get; set; } - - - - - - - - - - - .WriteNamedBitList(); - - - - - - - - = .ReadNamedBitListValue<>(); - - - Asn1Tag.PrimitiveBitString - - - public ReadOnlyMemory<byte>? { get; set; } - - - ReadOnlyMemory<byte> - - - - - - - - .WriteOctetString(.Value.Span); - - - - - - - - - if (.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory<byte> tmp)) - { - = tmp; - } - else - { - = .ReadOctetString(); - } - - - Asn1Tag.PrimitiveOctetString - - - public Oid { get; set; } - - - - public string { get; set; } - - - Oid - string - - - - - - - - - - .WriteObjectIdentifier(); - - - - - - - - - - - - = .ReadObjectIdentifier(); - - - - - - - - - - - - = .ReadObjectIdentifierAsString(); - - - - Asn1Tag.ObjectIdentifier - - - public { get; set; } - - - - - - - - - - - .WriteEnumeratedValue(); - - - - - - - - = .ReadEnumeratedValue<>(); - - - Asn1Tag.Enumerated - - - - public string { get; set; } - - - string - - - - - - - .WriteCharacterString(UniversalTagNumber., ); - - - - - - - - = .ReadCharacterString(UniversalTagNumber.); - - - new Asn1Tag(UniversalTagNumber.) - - - public [] { get; set; } - - - - - - - - SetOf - Sequence - - - - .Push(); - - for (int i = 0; i < .Length; i++) - { - } - - .Pop(); - - - - - - - - - SetOf - Sequence - - - - // Decode SEQUENCE OF for - { - collectionReader = .Read(); - var tmpList = new List<>(); - tmpItem; - - while (collectionReader.HasData) - { - tmpList.Add(tmpItem); - } - - decoded. = tmpList.ToArray(); - } - - - Asn1Tag.Sequence - - Asn1Tag.SetOf - - - public DateTimeOffset? { get; set; } - - - DateTimeOffset - - - - - - - - .WriteUtcTime(); - - - - - - - - - = .ReadUtcTime(); - - = .ReadUtcTime(); - - - - Asn1Tag.UtcTime - - - - - - - - - .WriteGeneralizedTime(.Value, ); - - .WriteGeneralizedTime(.Value); - - - - - - - - - - = .ReadGeneralizedTime(); - - = .ReadGeneralizedTime(); - - - - Asn1Tag.GeneralizedTime - - - - - - , - - - , - - - new Asn1Tag(TagClass.TagClass.ContextSpecific, ) - - s_default - - - else - { - defaultReader = new AsnReader(, AsnEncodingRules.DER); - } - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.generated.cs b/Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.generated.cs deleted file mode 100644 index 3cc172eb..00000000 --- a/Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.generated.cs +++ /dev/null @@ -1,179 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbChangePasswdData - { - /* - ChangePasswdData ::= SEQUENCE { - newpasswd [0] OCTET STRING, - targname [1] PrincipalName OPTIONAL, - targrealm [2] Realm OPTIONAL - } - */ - - public ReadOnlyMemory NewPasswd { get; set; } - - public KrbPrincipalName TargName { get; set; } - - public string TargRealm { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteOctetString(NewPasswd.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(TargName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - TargName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(TargRealm)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, TargRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbChangePasswdData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbChangePasswdData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbChangePasswdData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbChangePasswdData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbChangePasswdData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbChangePasswdData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbChangePasswdData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbChangePasswdData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpNewPasswd)) - { - decoded.NewPasswd = tmpNewPasswd; - } - else - { - decoded.NewPasswd = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpTargName); - decoded.TargName = tmpTargName; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - decoded.TargRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.xml b/Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.xml deleted file mode 100644 index f2e3f64b..00000000 --- a/Kerberos.NET/Entities/ChangePassword/KrbChangePasswdData.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.cs b/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.cs index fd1eaaaf..e65d1807 100644 --- a/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.cs +++ b/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.cs @@ -47,7 +47,7 @@ public static KdcProxyMessage WrapMessage( var proxyMessage = new KdcProxyMessage() { TargetDomain = domain, - DcLocatorHint = hint + DcLocatorHint = hint ?? default }; if (mode == KdcProxyMessageMode.NoPrefix) diff --git a/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.generated.cs b/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.generated.cs deleted file mode 100644 index 51934eb3..00000000 --- a/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.generated.cs +++ /dev/null @@ -1,187 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KdcProxyMessage - { - /* - KDC-PROXY-MESSAGE::= SEQUENCE { - kerb-message [0] OCTET STRING, - target-domain [1] KERB-REALM OPTIONAL, - dclocator-hint [2] INTEGER OPTIONAL - } - */ - - public ReadOnlyMemory KerbMessage { get; set; } - - public string TargetDomain { get; set; } - - public DcLocatorHint? DcLocatorHint { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteOctetString(KerbMessage.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(TargetDomain)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, TargetDomain); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - - if (Asn1Extension.HasValue(DcLocatorHint)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteInteger((long)DcLocatorHint.Value); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KdcProxyMessage Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KdcProxyMessage Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KdcProxyMessage Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KdcProxyMessage decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KdcProxyMessage Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KdcProxyMessage decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KdcProxyMessage, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KdcProxyMessage, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpKerbMessage)) - { - decoded.KerbMessage = tmpKerbMessage; - } - else - { - decoded.KerbMessage = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - decoded.TargetDomain = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - - if (explicitReader.TryReadInt32(out int tmpDcLocatorHint)) - { - decoded.DcLocatorHint = (DcLocatorHint)tmpDcLocatorHint; - } - else - { - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.xml b/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.xml deleted file mode 100644 index 5c8f50fd..00000000 --- a/Kerberos.NET/Entities/Kkdcp/KdcProxyMessage.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/IAKerbHeader.generated.cs b/Kerberos.NET/Entities/Krb/IAKerbHeader.generated.cs deleted file mode 100644 index fe41d9c1..00000000 --- a/Kerberos.NET/Entities/Krb/IAKerbHeader.generated.cs +++ /dev/null @@ -1,195 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class IAKerbHeader - { - /* - IAKERB-HEADER ::= SEQUENCE { - - - Note that the tag numbers start at 1, not 0, which would - - - be more conventional for Kerberos. - - target-realm [1] UTF8String, - - - The name of the target realm. - - cookie [2] OCTET STRING OPTIONAL, - - - Opaque data, if sent by the server, - - - MUST be copied by the client verbatim into - - - the next IAKRB_PROXY message. - - header-flags [3] BIT STRING OPTIONAL, - ... - } - */ - - public string TargetRealm { get; set; } - - public ReadOnlyMemory? Cookie { get; set; } - - public int? HeaderFlags { get; set; } - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.UTF8String, TargetRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (Asn1Extension.HasValue(Cookie)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(Cookie.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - - if (Asn1Extension.HasValue(HeaderFlags)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteBitString(HeaderFlags.Value.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static IAKerbHeader Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static IAKerbHeader Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static IAKerbHeader Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out IAKerbHeader decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static IAKerbHeader Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out IAKerbHeader decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: IAKerbHeader, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: IAKerbHeader, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.TargetRealm = explicitReader.ReadCharacterString(UniversalTagNumber.UTF8String); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpCookie)) - { - decoded.Cookie = tmpCookie; - } - else - { - decoded.Cookie = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpHeaderFlags)) - { - decoded.HeaderFlags = (int)tmpHeaderFlags.AsLong(); - } - else - { - decoded.HeaderFlags = (int)explicitReader.ReadBitString(out _).AsLong(); - } - - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/IAKerbHeader.xml b/Kerberos.NET/Entities/Krb/IAKerbHeader.xml deleted file mode 100644 index 7fc0d8db..00000000 --- a/Kerberos.NET/Entities/Krb/IAKerbHeader.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbApRep.xml b/Kerberos.NET/Entities/Krb/KrbApRep.xml deleted file mode 100644 index b5932d54..00000000 --- a/Kerberos.NET/Entities/Krb/KrbApRep.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbApReq.xml b/Kerberos.NET/Entities/Krb/KrbApReq.xml deleted file mode 100644 index 922b4a84..00000000 --- a/Kerberos.NET/Entities/Krb/KrbApReq.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAsRep.xml b/Kerberos.NET/Entities/Krb/KrbAsRep.xml deleted file mode 100644 index bc2bf32a..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAsRep.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAsReq.xml b/Kerberos.NET/Entities/Krb/KrbAsReq.xml deleted file mode 100644 index 66d712a9..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAsReq.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAuthenticator.xml b/Kerberos.NET/Entities/Krb/KrbAuthenticator.xml deleted file mode 100644 index 9f136899..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAuthenticator.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAuthorizationData.xml b/Kerberos.NET/Entities/Krb/KrbAuthorizationData.xml deleted file mode 100644 index 580ffc2d..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAuthorizationData.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.generated.cs b/Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.generated.cs deleted file mode 100644 index f71338d1..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.generated.cs +++ /dev/null @@ -1,151 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAuthorizationDataSequence - { - /* - AuthorizationData ::= SEQUENCE OF SEQUENCE { - ad-type [0] Int32, - ad-data [1] OCTET STRING - } - */ - - public KrbAuthorizationData[] AuthorizationData { get; set; } - -#if DEBUG - static KrbAuthorizationDataSequence() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(Asn1Tag.Sequence, "AuthorizationData"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (AuthorizationData != null) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(); - - for (int i = 0; i < AuthorizationData.Length; i++) - { - AuthorizationData[i]?.Encode(writer); - } - - writer.PopSequence(); - - wroteValue = true; - } - - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbAuthorizationDataSequence Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbAuthorizationDataSequence Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbAuthorizationDataSequence decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbAuthorizationDataSequence, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader collectionReader; - - if (tag.HasSameClassAndValue(Asn1Tag.Sequence)) - { - // Decode SEQUENCE OF for AuthorizationData - { - collectionReader = reader.ReadSequence(); - var tmpList = new List(); - KrbAuthorizationData tmpItem; - - while (collectionReader.HasData) - { - KrbAuthorizationData.Decode(collectionReader, out KrbAuthorizationData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.AuthorizationData = tmpList.ToArray(); - } - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.xml b/Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.xml deleted file mode 100644 index 0e652efe..00000000 --- a/Kerberos.NET/Entities/Krb/KrbAuthorizationDataSequence.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbChecksum.xml b/Kerberos.NET/Entities/Krb/KrbChecksum.xml deleted file mode 100644 index 9b163e62..00000000 --- a/Kerberos.NET/Entities/Krb/KrbChecksum.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbCred.xml b/Kerberos.NET/Entities/Krb/KrbCred.xml deleted file mode 100644 index e623ff57..00000000 --- a/Kerberos.NET/Entities/Krb/KrbCred.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbCredInfo.xml b/Kerberos.NET/Entities/Krb/KrbCredInfo.xml deleted file mode 100644 index edcc75e3..00000000 --- a/Kerberos.NET/Entities/Krb/KrbCredInfo.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbETypeInfo2.generated.cs b/Kerberos.NET/Entities/Krb/KrbETypeInfo2.generated.cs deleted file mode 100644 index 7beb2dad..00000000 --- a/Kerberos.NET/Entities/Krb/KrbETypeInfo2.generated.cs +++ /dev/null @@ -1,148 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbETypeInfo2 - { - /* - ETYPE-INFO2 ::= SEQUENCE SIZE (1..MAX) OF ETYPE-INFO2-ENTRY - */ - - public KrbETypeInfo2Entry[] ETypeInfo { get; set; } - -#if DEBUG - static KrbETypeInfo2() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(Asn1Tag.Sequence, "ETypeInfo"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (ETypeInfo != null) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(); - - for (int i = 0; i < ETypeInfo.Length; i++) - { - ETypeInfo[i]?.Encode(writer); - } - - writer.PopSequence(); - - wroteValue = true; - } - - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbETypeInfo2 Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbETypeInfo2 Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbETypeInfo2 decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbETypeInfo2, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader collectionReader; - - if (tag.HasSameClassAndValue(Asn1Tag.Sequence)) - { - // Decode SEQUENCE OF for ETypeInfo - { - collectionReader = reader.ReadSequence(); - var tmpList = new List(); - KrbETypeInfo2Entry tmpItem; - - while (collectionReader.HasData) - { - KrbETypeInfo2Entry.Decode(collectionReader, out KrbETypeInfo2Entry tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.ETypeInfo = tmpList.ToArray(); - } - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbETypeInfo2.xml b/Kerberos.NET/Entities/Krb/KrbETypeInfo2.xml deleted file mode 100644 index 203b44b1..00000000 --- a/Kerberos.NET/Entities/Krb/KrbETypeInfo2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.xml b/Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.xml deleted file mode 100644 index f593b96a..00000000 --- a/Kerberos.NET/Entities/Krb/KrbETypeInfo2Entry.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbETypeList.generated.cs b/Kerberos.NET/Entities/Krb/KrbETypeList.generated.cs deleted file mode 100644 index 7b40e323..00000000 --- a/Kerberos.NET/Entities/Krb/KrbETypeList.generated.cs +++ /dev/null @@ -1,157 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbETypeList - { - /* - EtypeList ::= SEQUENCE OF Int32 - - - Specifies the enctypes supported by the client. - - - This enctype list is in decreasing preference order - - - (favorite choice first). - - - Int32 is defined in [RFC4120]. - */ - - public EncryptionType[] List { get; set; } - -#if DEBUG - static KrbETypeList() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(Asn1Tag.Sequence, "List"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (List != null) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(); - - for (int i = 0; i < List.Length; i++) - { - writer.WriteInteger((long)List[i]); - } - - writer.PopSequence(); - - wroteValue = true; - } - - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbETypeList Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbETypeList Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbETypeList decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbETypeList, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader collectionReader; - - if (tag.HasSameClassAndValue(Asn1Tag.Sequence)) - { - // Decode SEQUENCE OF for List - { - collectionReader = reader.ReadSequence(); - var tmpList = new List(); - EncryptionType tmpItem; - - while (collectionReader.HasData) - { - - if (!collectionReader.TryReadInt32(out EncryptionType tmp)) - { - collectionReader.ThrowIfNotEmpty(); - } - - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.List = tmpList.ToArray(); - } - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbETypeList.xml b/Kerberos.NET/Entities/Krb/KrbETypeList.xml deleted file mode 100644 index b44aa705..00000000 --- a/Kerberos.NET/Entities/Krb/KrbETypeList.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncApRepPart.xml b/Kerberos.NET/Entities/Krb/KrbEncApRepPart.xml deleted file mode 100644 index b95598f5..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncApRepPart.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncAsRepPart.xml b/Kerberos.NET/Entities/Krb/KrbEncAsRepPart.xml deleted file mode 100644 index 2f8677b4..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncAsRepPart.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.xml b/Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.xml deleted file mode 100644 index 8ae00a98..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncKdcRepPart.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.xml b/Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.xml deleted file mode 100644 index 1734c6a9..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncKrbCredPart.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.xml b/Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.xml deleted file mode 100644 index 4ebc274f..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncKrbPrivPart.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.xml b/Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.xml deleted file mode 100644 index a29c456e..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncTgsRepPart.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncTicketPart.xml b/Kerberos.NET/Entities/Krb/KrbEncTicketPart.xml deleted file mode 100644 index e3da31d0..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncTicketPart.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncryptedData.xml b/Kerberos.NET/Entities/Krb/KrbEncryptedData.xml deleted file mode 100644 index f1f34e31..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncryptedData.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbEncryptionKey.xml b/Kerberos.NET/Entities/Krb/KrbEncryptionKey.xml deleted file mode 100644 index 77c9aabd..00000000 --- a/Kerberos.NET/Entities/Krb/KrbEncryptionKey.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbError.xml b/Kerberos.NET/Entities/Krb/KrbError.xml deleted file mode 100644 index ca7ca474..00000000 --- a/Kerberos.NET/Entities/Krb/KrbError.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbErrorData.generated.cs b/Kerberos.NET/Entities/Krb/KrbErrorData.generated.cs deleted file mode 100644 index 0efa1b77..00000000 --- a/Kerberos.NET/Entities/Krb/KrbErrorData.generated.cs +++ /dev/null @@ -1,158 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbErrorData - { - /* - KERB-ERROR-DATA ::= SEQUENCE { - data-type [1] INTEGER, - data-value [2] OCTET STRING OPTIONAL - } - */ - - public KrbErrorDataType Type { get; set; } - - public ReadOnlyMemory Value { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbErrorData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbErrorData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbErrorData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbErrorData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbErrorData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbErrorData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbErrorData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbErrorData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out KrbErrorDataType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpValue)) - { - decoded.Value = tmpValue; - } - else - { - decoded.Value = explicitReader.ReadOctetString(); - } - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbErrorData.xml b/Kerberos.NET/Entities/Krb/KrbErrorData.xml deleted file mode 100644 index 50d7852f..00000000 --- a/Kerberos.NET/Entities/Krb/KrbErrorData.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbHostAddress.xml b/Kerberos.NET/Entities/Krb/KrbHostAddress.xml deleted file mode 100644 index 96b5f6ef..00000000 --- a/Kerberos.NET/Entities/Krb/KrbHostAddress.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbKdcRep.xml b/Kerberos.NET/Entities/Krb/KrbKdcRep.xml deleted file mode 100644 index 31252ee6..00000000 --- a/Kerberos.NET/Entities/Krb/KrbKdcRep.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbKdcReq.xml b/Kerberos.NET/Entities/Krb/KrbKdcReq.xml deleted file mode 100644 index bd98b8be..00000000 --- a/Kerberos.NET/Entities/Krb/KrbKdcReq.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbKdcReqBody.xml b/Kerberos.NET/Entities/Krb/KrbKdcReqBody.xml deleted file mode 100644 index 0fdfcb55..00000000 --- a/Kerberos.NET/Entities/Krb/KrbKdcReqBody.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbLastReq.xml b/Kerberos.NET/Entities/Krb/KrbLastReq.xml deleted file mode 100644 index b6363421..00000000 --- a/Kerberos.NET/Entities/Krb/KrbLastReq.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbMethodData.generated.cs b/Kerberos.NET/Entities/Krb/KrbMethodData.generated.cs deleted file mode 100644 index 92b552e3..00000000 --- a/Kerberos.NET/Entities/Krb/KrbMethodData.generated.cs +++ /dev/null @@ -1,148 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbMethodData - { - /* - METHOD-DATA ::= SEQUENCE OF PA-DATA - */ - - public KrbPaData[] MethodData { get; set; } - -#if DEBUG - static KrbMethodData() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(Asn1Tag.Sequence, "MethodData"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (MethodData != null) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(); - - for (int i = 0; i < MethodData.Length; i++) - { - MethodData[i]?.Encode(writer); - } - - writer.PopSequence(); - - wroteValue = true; - } - - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbMethodData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbMethodData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbMethodData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbMethodData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader collectionReader; - - if (tag.HasSameClassAndValue(Asn1Tag.Sequence)) - { - // Decode SEQUENCE OF for MethodData - { - collectionReader = reader.ReadSequence(); - var tmpList = new List(); - KrbPaData tmpItem; - - while (collectionReader.HasData) - { - KrbPaData.Decode(collectionReader, out KrbPaData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.MethodData = tmpList.ToArray(); - } - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbMethodData.xml b/Kerberos.NET/Entities/Krb/KrbMethodData.xml deleted file mode 100644 index 57b86195..00000000 --- a/Kerberos.NET/Entities/Krb/KrbMethodData.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaData.xml b/Kerberos.NET/Entities/Krb/KrbPaData.xml deleted file mode 100644 index 2def505b..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaData.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.xml b/Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.xml deleted file mode 100644 index f36d5a5a..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaEncTsEnc.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaForUser.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaForUser.generated.cs deleted file mode 100644 index b8579a55..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaForUser.generated.cs +++ /dev/null @@ -1,168 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaForUser - { - /* - PA-FOR-USER ::= SEQUENCE { - userName [0] PrincipalName, - userRealm [1] Realm, - cksum [2] Checksum, - auth-package [3] KerberosString - } - */ - - public KrbPrincipalName UserName { get; set; } - - public string UserRealm { get; set; } - - public KrbChecksum Checksum { get; set; } - - public string AuthPackage { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - UserName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, UserRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - Checksum?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, AuthPackage); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaForUser Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaForUser Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaForUser Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaForUser decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaForUser Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaForUser decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaForUser, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaForUser, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpUserName); - decoded.UserName = tmpUserName; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - decoded.UserRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - KrbChecksum.Decode(explicitReader, out KrbChecksum tmpChecksum); - decoded.Checksum = tmpChecksum; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - decoded.AuthPackage = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaForUser.xml b/Kerberos.NET/Entities/Krb/KrbPaForUser.xml deleted file mode 100644 index c92b69bf..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaForUser.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaPacOptions.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaPacOptions.generated.cs deleted file mode 100644 index 347a33cc..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaPacOptions.generated.cs +++ /dev/null @@ -1,146 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaPacOptions - { - /* - PA-PAC-OPTIONS ::= SEQUENCE { - KerberosFlags - - - Claims (0) - - - Branch Aware (1) - - - Forward to Full DC (2) - } - Note: KerberosFlags ::= BIT STRING (SIZE (32..MAX)) - - - minimum number of bits shall be sent, but no fewer than 32 - */ - - public PacOptions Flags { get; set; } - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteBitString(Flags.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaPacOptions Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaPacOptions Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaPacOptions Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaPacOptions decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaPacOptions Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaPacOptions decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaPacOptions, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaPacOptions, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpFlags)) - { - decoded.Flags = (PacOptions)tmpFlags.AsLong(); - } - else - { - decoded.Flags = (PacOptions)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaPacOptions.xml b/Kerberos.NET/Entities/Krb/KrbPaPacOptions.xml deleted file mode 100644 index fc472de1..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaPacOptions.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaPacRequest.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaPacRequest.generated.cs deleted file mode 100644 index bae781d5..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaPacRequest.generated.cs +++ /dev/null @@ -1,134 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaPacRequest - { - /* - KERB-PA-PAC-REQUEST ::= SEQUENCE { - include-pac[0] BOOLEAN - -If TRUE, and no pac present, include PAC. - - -If FALSE, and PAC present, remove PAC - } - */ - - public bool IncludePac { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteBoolean(IncludePac); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaPacRequest Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaPacRequest Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaPacRequest Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaPacRequest decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaPacRequest Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaPacRequest decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaPacRequest, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaPacRequest, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - decoded.IncludePac = explicitReader.ReadBoolean(); - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaPacRequest.xml b/Kerberos.NET/Entities/Krb/KrbPaPacRequest.xml deleted file mode 100644 index 4581311b..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaPacRequest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaS4uX509User.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaS4uX509User.generated.cs deleted file mode 100644 index 4262684b..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaS4uX509User.generated.cs +++ /dev/null @@ -1,159 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaS4uX509User - { - /* - - PA-S4U-X509-USER::= SEQUENCE { - user-id[0] S4UUserID, - checksum[1] Checksum - } - - S4UUserID ::= SEQUENCE { - nonce [0] UInt32, - - the nonce in KDC-REQ-BODY - cname [1] PrincipalName OPTIONAL, - - - Certificate mapping hints - crealm [2] Realm, - subject-certificate [3] OCTET STRING OPTIONAL, - options [4] BIT STRING OPTIONAL, - ... - } - - - */ - - public KrbS4uUserId UserId { get; set; } - - public KrbChecksum Checksum { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - UserId?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - Checksum?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaS4uX509User Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaS4uX509User Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaS4uX509User Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaS4uX509User decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaS4uX509User Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaS4uX509User decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaS4uX509User, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaS4uX509User, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbS4uUserId.Decode(explicitReader, out KrbS4uUserId tmpUserId); - decoded.UserId = tmpUserId; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - KrbChecksum.Decode(explicitReader, out KrbChecksum tmpChecksum); - decoded.Checksum = tmpChecksum; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaS4uX509User.xml b/Kerberos.NET/Entities/Krb/KrbPaS4uX509User.xml deleted file mode 100644 index c211c757..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaS4uX509User.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs b/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs deleted file mode 100644 index 6818c760..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.generated.cs +++ /dev/null @@ -1,154 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaSvrReferralData - { - /* - PA-SVR-REFERRAL-INFO 20 - - PA-SVR-REFERRAL-DATA ::= SEQUENCE { - referred-name [1] PrincipalName OPTIONAL, - referred-realm [0] Realm - } - */ - - public KrbPrincipalName ReferredName { get; set; } - - public string ReferredRealm { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - - if (Asn1Extension.HasValue(ReferredName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - ReferredName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, ReferredRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaSvrReferralData Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaSvrReferralData Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaSvrReferralData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaSvrReferralData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaSvrReferralData Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaSvrReferralData decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaSvrReferralData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaSvrReferralData, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpReferredName); - decoded.ReferredName = tmpReferredName; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - decoded.ReferredRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.xml b/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.xml deleted file mode 100644 index 608a26bd..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPaSvrReferralData.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPrincipalName.xml b/Kerberos.NET/Entities/Krb/KrbPrincipalName.xml deleted file mode 100644 index ffe38c5f..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPrincipalName.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbPriv.xml b/Kerberos.NET/Entities/Krb/KrbPriv.xml deleted file mode 100644 index 29cc7836..00000000 --- a/Kerberos.NET/Entities/Krb/KrbPriv.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbS4uUserId.generated.cs b/Kerberos.NET/Entities/Krb/KrbS4uUserId.generated.cs deleted file mode 100644 index f74bc4de..00000000 --- a/Kerberos.NET/Entities/Krb/KrbS4uUserId.generated.cs +++ /dev/null @@ -1,224 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbS4uUserId - { - /* - - PA-S4U-X509-USER::= SEQUENCE { - user-id[0] S4UUserID, - checksum[1] Checksum - } - - S4UUserID ::= SEQUENCE { - nonce [0] UInt32, - - the nonce in KDC-REQ-BODY - cname [1] PrincipalName OPTIONAL, - - - Certificate mapping hints - crealm [2] Realm, - subject-certificate [3] OCTET STRING OPTIONAL, - options [4] BIT STRING OPTIONAL, - ... - } - - - */ - - public int Nonce { get; set; } - - public KrbPrincipalName CName { get; set; } - - public string Realm { get; set; } - - public ReadOnlyMemory? SubjectCertificate { get; set; } - - public S4uOptions Options { get; set; } - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger(Nonce); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(CName)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, Realm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - if (Asn1Extension.HasValue(SubjectCertificate)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteOctetString(SubjectCertificate.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.WriteBitString(Options.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbS4uUserId Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbS4uUserId Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbS4uUserId Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbS4uUserId decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbS4uUserId Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbS4uUserId decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbS4uUserId, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbS4uUserId, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out int tmpNonce)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Nonce = tmpNonce; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - decoded.Realm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpSubjectCertificate)) - { - decoded.SubjectCertificate = tmpSubjectCertificate; - } - else - { - decoded.SubjectCertificate = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpOptions)) - { - decoded.Options = (S4uOptions)tmpOptions.AsLong(); - } - else - { - decoded.Options = (S4uOptions)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Krb/KrbS4uUserId.xml b/Kerberos.NET/Entities/Krb/KrbS4uUserId.xml deleted file mode 100644 index 348f30df..00000000 --- a/Kerberos.NET/Entities/Krb/KrbS4uUserId.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbTgsRep.xml b/Kerberos.NET/Entities/Krb/KrbTgsRep.xml deleted file mode 100644 index a729a6c1..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTgsRep.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbTgsReq.xml b/Kerberos.NET/Entities/Krb/KrbTgsReq.xml deleted file mode 100644 index 5a27368d..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTgsReq.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbTicket.xml b/Kerberos.NET/Entities/Krb/KrbTicket.xml deleted file mode 100644 index 3ac3b7c5..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTicket.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/KrbTransitedEncoding.xml b/Kerberos.NET/Entities/Krb/KrbTransitedEncoding.xml deleted file mode 100644 index b3dbb65e..00000000 --- a/Kerberos.NET/Entities/Krb/KrbTransitedEncoding.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.generated.cs deleted file mode 100644 index 9434b73a..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.generated.cs +++ /dev/null @@ -1,140 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbAlgorithmIdentifier - { - /* - AlgorithmIdentifier ::= SEQUENCE { - algorithm OBJECT IDENTIFIER, - parameters ANY DEFINED BY algorithm OPTIONAL - } - */ - - public Oid Algorithm { get; set; } - - public ReadOnlyMemory? Parameters { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.WriteObjectIdentifier(Algorithm); - - if (Asn1Extension.HasValue(Parameters)) - { - writer.WriteEncodedValue(Parameters.Value.Span); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbAlgorithmIdentifier Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbAlgorithmIdentifier Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbAlgorithmIdentifier Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbAlgorithmIdentifier decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbAlgorithmIdentifier Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbAlgorithmIdentifier decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbAlgorithmIdentifier, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbAlgorithmIdentifier, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - - decoded.Algorithm = sequenceReader.ReadObjectIdentifier(); - - if (sequenceReader.HasData) - { - decoded.Parameters = sequenceReader.ReadEncodedValue(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.xml b/Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.xml deleted file mode 100644 index 2b32e9ff..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbAlgorithmIdentifier.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbAuthPack.xml b/Kerberos.NET/Entities/Pkinit/KrbAuthPack.xml deleted file mode 100644 index 0f84ad0b..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbAuthPack.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.xml b/Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.xml deleted file mode 100644 index 918876e0..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbDHReplyInfo.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.generated.cs deleted file mode 100644 index 63e4c177..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.generated.cs +++ /dev/null @@ -1,165 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbDiffieHellmanDomainParameters - { - /* - DomainParameters ::= SEQUENCE { - p INTEGER, - - odd prime, p=jq +1 - g INTEGER, - - generator, g - q INTEGER, - - factor of p-1 - j INTEGER OPTIONAL, - - subgroup factor - validationParms ValidationParms OPTIONAL - } - */ - - public ReadOnlyMemory P { get; set; } - - public ReadOnlyMemory G { get; set; } - - public ReadOnlyMemory Q { get; set; } - - public ReadOnlyMemory? J { get; set; } - - public KrbDiffieHellmanValidationParameters ValidationParameters { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.WriteEncodedValue(P.Span); - writer.WriteEncodedValue(G.Span); - writer.WriteEncodedValue(Q.Span); - - if (Asn1Extension.HasValue(J)) - { - writer.WriteEncodedValue(J.Value.Span); - } - - if (Asn1Extension.HasValue(ValidationParameters)) - { - ValidationParameters?.Encode(writer); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbDiffieHellmanDomainParameters Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbDiffieHellmanDomainParameters Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbDiffieHellmanDomainParameters Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbDiffieHellmanDomainParameters decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbDiffieHellmanDomainParameters Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbDiffieHellmanDomainParameters decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbDiffieHellmanDomainParameters, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbDiffieHellmanDomainParameters, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - - decoded.P = sequenceReader.ReadEncodedValue(); - decoded.G = sequenceReader.ReadEncodedValue(); - decoded.Q = sequenceReader.ReadEncodedValue(); - - if (sequenceReader.HasData) - { - decoded.J = sequenceReader.ReadEncodedValue(); - } - - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(Asn1Tag.Sequence)) - { - KrbDiffieHellmanValidationParameters.Decode(sequenceReader, out KrbDiffieHellmanValidationParameters tmpValidationParameters); - decoded.ValidationParameters = tmpValidationParameters; - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.xml b/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.xml deleted file mode 100644 index 6f54fed2..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanDomainParameters.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.generated.cs deleted file mode 100644 index 46ba8fb6..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.generated.cs +++ /dev/null @@ -1,140 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbDiffieHellmanValidationParameters - { - /* - ValidationParms ::= SEQUENCE { - seed BIT STRING, - pgenCounter INTEGER - } - */ - - public ReadOnlyMemory Seed { get; set; } - - public System.Numerics.BigInteger PGenOutput { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.WriteBitString(Seed.Span); - writer.WriteInteger(PGenOutput); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbDiffieHellmanValidationParameters Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbDiffieHellmanValidationParameters Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbDiffieHellmanValidationParameters Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbDiffieHellmanValidationParameters decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbDiffieHellmanValidationParameters Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbDiffieHellmanValidationParameters decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbDiffieHellmanValidationParameters, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbDiffieHellmanValidationParameters, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - - - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpSeed)) - { - decoded.Seed = tmpSeed; - } - else - { - decoded.Seed = sequenceReader.ReadBitString(out _); - } - - decoded.PGenOutput = sequenceReader.ReadInteger(); - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.xml b/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.xml deleted file mode 100644 index 8a743b0c..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbDiffieHellmanValidationParameters.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.xml b/Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.xml deleted file mode 100644 index 7066070b..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbExternalPrincipalIdentifier.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.xml b/Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.xml deleted file mode 100644 index 88078d16..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbKdcDHKeyInfo.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.xml b/Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.xml deleted file mode 100644 index 6022ed29..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbPKAuthenticator.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.xml b/Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.xml deleted file mode 100644 index fda65e9b..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsRep.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.xml b/Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.xml deleted file mode 100644 index 49b4c208..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbPaPkAsReq.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.generated.cs b/Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.generated.cs deleted file mode 100644 index 8206c87f..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.generated.cs +++ /dev/null @@ -1,141 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbSubjectPublicKeyInfo - { - /* - SubjectPublicKeyInfo ::= SEQUENCE { - algorithm AlgorithmIdentifier, - subjectPublicKey BIT STRING - } - */ - - public KrbAlgorithmIdentifier Algorithm { get; set; } - - public ReadOnlyMemory SubjectPublicKey { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - Algorithm?.Encode(writer); - writer.WriteBitString(SubjectPublicKey.Span); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbSubjectPublicKeyInfo Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbSubjectPublicKeyInfo Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbSubjectPublicKeyInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbSubjectPublicKeyInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbSubjectPublicKeyInfo Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbSubjectPublicKeyInfo decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbSubjectPublicKeyInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbSubjectPublicKeyInfo, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - - KrbAlgorithmIdentifier.Decode(sequenceReader, out KrbAlgorithmIdentifier tmpAlgorithm); - decoded.Algorithm = tmpAlgorithm; - - if (sequenceReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpSubjectPublicKey)) - { - decoded.SubjectPublicKey = tmpSubjectPublicKey; - } - else - { - decoded.SubjectPublicKey = sequenceReader.ReadBitString(out _); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.xml b/Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.xml deleted file mode 100644 index 6b0f6230..00000000 --- a/Kerberos.NET/Entities/Pkinit/KrbSubjectPublicKeyInfo.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.generated.cs deleted file mode 100644 index f5d975f6..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.generated.cs +++ /dev/null @@ -1,168 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbFastArmor - { - /* - KrbFastArmor ::= SEQUENCE { - armor-type [0] Int32, - - - Type of the armor. - armor-value [1] OCTET STRING, - - - Value of the armor. - ... - } - */ - - public KrbArmorType Type { get; set; } - - public ReadOnlyMemory? Value { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(Value)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(Value.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbFastArmor Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbFastArmor Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbFastArmor Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbFastArmor decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbFastArmor Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbFastArmor decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbFastArmor, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbFastArmor, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out KrbArmorType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpValue)) - { - decoded.Value = tmpValue; - } - else - { - decoded.Value = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.xml b/Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.xml deleted file mode 100644 index 4ddb717c..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmor.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.generated.cs deleted file mode 100644 index 7b29881b..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.generated.cs +++ /dev/null @@ -1,137 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbFastArmoredRep - { - /* - KrbFastArmoredRep ::= SEQUENCE { - enc-fast-rep [0] EncryptedData, - - KrbFastResponse - - - - - The encryption key is the armor key in the request, and - - - the key usage number is KEY_USAGE_FAST_REP. - ... - } - */ - - public KrbEncryptedData EncFastRep { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - EncFastRep?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbFastArmoredRep Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbFastArmoredRep Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbFastArmoredRep Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbFastArmoredRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbFastArmoredRep Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbFastArmoredRep decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbFastArmoredRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbFastArmoredRep, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncFastRep); - decoded.EncFastRep = tmpEncFastRep; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.xml b/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.xml deleted file mode 100644 index f47c6cc4..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredRep.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.generated.cs deleted file mode 100644 index 68a85b3d..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.generated.cs +++ /dev/null @@ -1,179 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbFastArmoredReq - { - /* - KrbFastArmoredReq ::= SEQUENCE { - armor [0] KrbFastArmor OPTIONAL, - - - Contains the armor that identifies the armor key. - - - MUST be present in AS-REQ. - req-checksum [1] Checksum, - - - For AS, contains the checksum performed over the type - - - KDC-REQ-BODY for the req-body field of the KDC-REQ - - - structure; - - - For TGS, contains the checksum performed over the type - - - AP-REQ in the PA-TGS-REQ padata. - - - The checksum key is the armor key, the checksum - - - type is the required checksum type for the enctype of - - - the armor key, and the key usage number is - - - KEY_USAGE_FAST_REQ_CHKSUM. - enc-fast-req [2] EncryptedData, - - KrbFastReq - - - - - The encryption key is the armor key, and the key usage - - - number is KEY_USAGE_FAST_ENC. - ... - } - */ - - public KrbFastArmor Armor { get; set; } - - public KrbChecksum RequestChecksum { get; set; } - - public KrbEncryptedData EncryptedFastRequest { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - - if (Asn1Extension.HasValue(Armor)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - Armor?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - RequestChecksum?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - EncryptedFastRequest?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbFastArmoredReq Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbFastArmoredReq Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbFastArmoredReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbFastArmoredReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbFastArmoredReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbFastArmoredReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbFastArmoredReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbFastArmoredReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - KrbFastArmor.Decode(explicitReader, out KrbFastArmor tmpArmor); - decoded.Armor = tmpArmor; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - KrbChecksum.Decode(explicitReader, out KrbChecksum tmpRequestChecksum); - decoded.RequestChecksum = tmpRequestChecksum; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - KrbEncryptedData.Decode(explicitReader, out KrbEncryptedData tmpEncryptedFastRequest); - decoded.EncryptedFastRequest = tmpEncryptedFastRequest; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.xml b/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.xml deleted file mode 100644 index e11befc1..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastArmoredReq.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.generated.cs deleted file mode 100644 index 91ab1caf..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.generated.cs +++ /dev/null @@ -1,193 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbFastFinished - { - /* - KrbFastFinished ::= SEQUENCE { - timestamp [0] KerberosTime, - usec [1] Microseconds, - - - timestamp and usec represent the time on the KDC when - - - the reply was generated. - crealm [2] Realm, - cname [3] PrincipalName, - - - Contains the client realm and the client name. - ticket-checksum [4] Checksum, - - - checksum of the ticket in the KDC-REP using the armor - - - and the key usage is KEY_USAGE_FAST_FINISH. - - - The checksum type is the required checksum type - - - of the armor key. - ... - } - */ - - public DateTimeOffset Timestamp { get; set; } - - public int USec { get; set; } - - public string CRealm { get; set; } - - public KrbPrincipalName CName { get; set; } - - public KrbChecksum TicketChecksum { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteGeneralizedTime(Timestamp); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteInteger(USec); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteCharacterString(UniversalTagNumber.GeneralString, CRealm); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - CName?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - TicketChecksum?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbFastFinished Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbFastFinished Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbFastFinished Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbFastFinished decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbFastFinished Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbFastFinished decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbFastFinished, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbFastFinished, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - decoded.Timestamp = explicitReader.ReadGeneralizedTime(); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - if (!explicitReader.TryReadInt32(out int tmpUSec)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.USec = tmpUSec; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - decoded.CRealm = explicitReader.ReadCharacterString(UniversalTagNumber.GeneralString); - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - KrbPrincipalName.Decode(explicitReader, out KrbPrincipalName tmpCName); - decoded.CName = tmpCName; - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); - KrbChecksum.Decode(explicitReader, out KrbChecksum tmpTicketChecksum); - decoded.TicketChecksum = tmpTicketChecksum; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.xml b/Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.xml deleted file mode 100644 index 2a950b19..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastFinished.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastReq.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbFastReq.generated.cs deleted file mode 100644 index 16df58f4..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastReq.generated.cs +++ /dev/null @@ -1,195 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbFastReq - { - /* - KrbFastReq ::= SEQUENCE { - fast-options [0] FastOptions, - - - Additional options. - padata [1] SEQUENCE OF PA-DATA, - - - padata typed holes. - req-body [2] KDC-REQ-BODY, - - - Contains the KDC request body as defined in Section - - - 5.4.1 of [RFC4120]. - - - This req-body field is preferred over the outer field - - - in the KDC request. - ... - } - */ - - public FastOptions FastOptions { get; set; } - public KrbPaData[] PaData { get; set; } - - public KrbKdcReqBody ReqBody { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteBitString(FastOptions.AsReadOnlySpan()); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(); - - for (int i = 0; i < PaData.Length; i++) - { - PaData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - ReqBody?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbFastReq Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbFastReq Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbFastReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbFastReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbFastReq Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbFastReq decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbFastReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbFastReq, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (explicitReader.TryReadPrimitiveBitStringValue(out _, out ReadOnlyMemory tmpFastOptions)) - { - decoded.FastOptions = (FastOptions)tmpFastOptions.AsLong(); - } - else - { - decoded.FastOptions = (FastOptions)explicitReader.ReadBitString(out _).AsLong(); - } - - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - // Decode SEQUENCE OF for PaData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbPaData tmpItem; - - while (collectionReader.HasData) - { - KrbPaData.Decode(collectionReader, out KrbPaData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.PaData = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - KrbKdcReqBody.Decode(explicitReader, out KrbKdcReqBody tmpReqBody); - decoded.ReqBody = tmpReqBody; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastReq.xml b/Kerberos.NET/Entities/PreAuthentication/KrbFastReq.xml deleted file mode 100644 index c9587de8..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastReq.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.generated.cs deleted file mode 100644 index 9d44add5..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.generated.cs +++ /dev/null @@ -1,219 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbFastResponse - { - /* - KrbFastResponse ::= SEQUENCE { - padata [0] SEQUENCE OF PA-DATA, - - - padata typed holes. - strengthen-key [1] EncryptionKey OPTIONAL, - - - This, if present, strengthens the reply key for AS and - - - TGS. MUST be present for TGS. - - - MUST be absent in KRB-ERROR. - finished [2] KrbFastFinished OPTIONAL, - - - Present in AS or TGS reply; absent otherwise. - nonce [3] UInt32, - - - Nonce from the client request. - ... - } - */ - - public KrbPaData[] PaData { get; set; } - - public KrbEncryptionKey StrengthenKey { get; set; } - - public KrbFastFinished Finished { get; set; } - - public int Nonce { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.PushSequence(); - - for (int i = 0; i < PaData.Length; i++) - { - PaData[i]?.Encode(writer); - } - - writer.PopSequence(); - - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(StrengthenKey)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - StrengthenKey?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(Finished)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - Finished?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.WriteInteger(Nonce); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbFastResponse Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbFastResponse Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbFastResponse Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbFastResponse decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbFastResponse Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbFastResponse decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbFastResponse, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbFastResponse, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - AsnReader collectionReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - // Decode SEQUENCE OF for PaData - { - collectionReader = explicitReader.ReadSequence(); - var tmpList = new List(); - KrbPaData tmpItem; - - while (collectionReader.HasData) - { - KrbPaData.Decode(collectionReader, out KrbPaData tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.PaData = tmpList.ToArray(); - } - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - KrbEncryptionKey.Decode(explicitReader, out KrbEncryptionKey tmpStrengthenKey); - decoded.StrengthenKey = tmpStrengthenKey; - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - KrbFastFinished.Decode(explicitReader, out KrbFastFinished tmpFinished); - decoded.Finished = tmpFinished; - explicitReader.ThrowIfNotEmpty(); - } - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); - - if (!explicitReader.TryReadInt32(out int tmpNonce)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Nonce = tmpNonce; - - explicitReader.ThrowIfNotEmpty(); - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.xml b/Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.xml deleted file mode 100644 index 5cccc4b3..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbFastResponse.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.generated.cs deleted file mode 100644 index 51ed4651..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.generated.cs +++ /dev/null @@ -1,156 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaAuthenticationSet - { - /* - PA-AUTHENTICATION-SET ::= SEQUENCE OF PA-AUTHENTICATION-SET-ELEM - - PA-AUTHENTICATION-SET-ELEM ::= SEQUENCE { - pa-type [0] Int32, - - - same as padata-type. - pa-hint [1] OCTET STRING OPTIONAL, - pa-value [2] OCTET STRING OPTIONAL, - ... - } - */ - - public KrbPaAuthenticationSetElement[] AuthenticationSet { get; set; } - -#if DEBUG - static KrbPaAuthenticationSet() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(Asn1Tag.Sequence, "AuthenticationSet"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (AuthenticationSet != null) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(); - - for (int i = 0; i < AuthenticationSet.Length; i++) - { - AuthenticationSet[i]?.Encode(writer); - } - - writer.PopSequence(); - - wroteValue = true; - } - - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaAuthenticationSet Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaAuthenticationSet Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbPaAuthenticationSet decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaAuthenticationSet, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader collectionReader; - - if (tag.HasSameClassAndValue(Asn1Tag.Sequence)) - { - // Decode SEQUENCE OF for AuthenticationSet - { - collectionReader = reader.ReadSequence(); - var tmpList = new List(); - KrbPaAuthenticationSetElement tmpItem; - - while (collectionReader.HasData) - { - KrbPaAuthenticationSetElement.Decode(collectionReader, out KrbPaAuthenticationSetElement tmp); - tmpItem = tmp; - tmpList.Add(tmpItem); - } - - decoded.AuthenticationSet = tmpList.ToArray(); - } - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.xml b/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.xml deleted file mode 100644 index 6e87ccb0..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSet.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.generated.cs deleted file mode 100644 index 197cb43f..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.generated.cs +++ /dev/null @@ -1,195 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaAuthenticationSetElement - { - /* - PA-AUTHENTICATION-SET ::= SEQUENCE OF PA-AUTHENTICATION-SET-ELEM - - PA-AUTHENTICATION-SET-ELEM ::= SEQUENCE { - pa-type [0] Int32, - - - same as padata-type. - pa-hint [1] OCTET STRING OPTIONAL, - pa-value [2] OCTET STRING OPTIONAL, - ... - } - */ - - public PaDataType Type { get; set; } - - public ReadOnlyMemory? Hint { get; set; } - - public ReadOnlyMemory? Value { get; set; } - - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - Encode(writer, Asn1Tag.Sequence); - } - - internal void Encode(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - writer.WriteInteger((long)Type); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (Asn1Extension.HasValue(Hint)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - writer.WriteOctetString(Hint.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - } - - if (Asn1Extension.HasValue(Value)) - { - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - writer.WriteOctetString(Value.Value.Span); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - } - writer.PopSequence(tag); - } - - internal void EncodeApplication(AsnWriter writer, Asn1Tag tag) - { - writer.PushSequence(tag); - - this.Encode(writer, Asn1Tag.Sequence); - - writer.PopSequence(tag); - } - - public virtual ReadOnlyMemory EncodeApplication() => new ReadOnlyMemory(); - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - EncodeApplication(writer, tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaAuthenticationSetElement Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaAuthenticationSetElement Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - return Decode(Asn1Tag.Sequence, encoded, ruleSet); - } - - internal static KrbPaAuthenticationSetElement Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded) - { - AsnReader reader = new AsnReader(encoded, AsnEncodingRules.DER); - - Decode(reader, expectedTag, out KrbPaAuthenticationSetElement decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static KrbPaAuthenticationSetElement Decode(Asn1Tag expectedTag, ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, expectedTag, out KrbPaAuthenticationSetElement decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaAuthenticationSetElement, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - Decode(reader, Asn1Tag.Sequence, out decoded); - } - - internal static void Decode(AsnReader reader, Asn1Tag expectedTag, out T decoded) - where T: KrbPaAuthenticationSetElement, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - AsnReader sequenceReader = reader.ReadSequence(expectedTag); - AsnReader explicitReader; - - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - - if (!explicitReader.TryReadInt32(out PaDataType tmpType)) - { - explicitReader.ThrowIfNotEmpty(); - } - - decoded.Type = tmpType; - - explicitReader.ThrowIfNotEmpty(); - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpHint)) - { - decoded.Hint = tmpHint; - } - else - { - decoded.Hint = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) - { - explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); - - - if (explicitReader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory tmpValue)) - { - decoded.Value = tmpValue; - } - else - { - decoded.Value = explicitReader.ReadOctetString(); - } - explicitReader.ThrowIfNotEmpty(); - } - - sequenceReader.ThrowIfNotEmpty(); - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.xml b/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.xml deleted file mode 100644 index 2a4d8617..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaAuthenticationSetElement.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.generated.cs deleted file mode 100644 index 19869466..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.generated.cs +++ /dev/null @@ -1,132 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaFxFastReply - { - /* - PA-FX-FAST-REPLY ::= CHOICE { - armored-data [0] KrbFastArmoredRep, - ... - } - */ - - public KrbFastArmoredRep ArmoredData { get; set; } - -#if DEBUG - static KrbPaFxFastReply() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 0), "ArmoredData"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (Asn1Extension.HasValue(ArmoredData)) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - ArmoredData?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - wroteValue = true; - } - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaFxFastReply Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaFxFastReply Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbPaFxFastReply decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaFxFastReply, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader explicitReader; - - if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbFastArmoredRep.Decode(explicitReader, out KrbFastArmoredRep tmpArmoredData); - decoded.ArmoredData = tmpArmoredData; - explicitReader.ThrowIfNotEmpty(); - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.xml b/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.xml deleted file mode 100644 index 0eda210d..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastReply.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.generated.cs b/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.generated.cs deleted file mode 100644 index f5e28e48..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.generated.cs +++ /dev/null @@ -1,132 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -// This is a generated file. -// The generation template has been modified from .NET Runtime implementation - -using System; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Security.Cryptography.Asn1; -using Kerberos.NET.Crypto; -using Kerberos.NET.Asn1; - -namespace Kerberos.NET.Entities -{ - public partial class KrbPaFxFastRequest - { - /* - PA-FX-FAST-REQUEST ::= CHOICE { - armored-data [0] KrbFastArmoredReq, - ... - } - */ - - public KrbFastArmoredReq ArmoredData { get; set; } - -#if DEBUG - static KrbPaFxFastRequest() - { - var usedTags = new System.Collections.Generic.Dictionary(); - Action ensureUniqueTag = (tag, fieldName) => - { - if (usedTags.TryGetValue(tag, out string existing)) - { - throw new InvalidOperationException($"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'"); - } - - usedTags.Add(tag, fieldName); - }; - - ensureUniqueTag(new Asn1Tag(TagClass.ContextSpecific, 0), "ArmoredData"); - } -#endif - // Encoding methods - public ReadOnlyMemory Encode() - { - var writer = new AsnWriter(AsnEncodingRules.DER); - - Encode(writer); - - return writer.EncodeAsMemory(); - } - - internal void Encode(AsnWriter writer) - { - bool wroteValue = false; - - if (Asn1Extension.HasValue(ArmoredData)) - { - if (wroteValue) - { - throw new CryptographicException(); - } - - writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - ArmoredData?.Encode(writer); - writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - wroteValue = true; - } - if (!wroteValue) - { - throw new CryptographicException(); - } - } - - internal ReadOnlyMemory EncodeApplication(Asn1Tag tag) - { - using (var writer = new AsnWriter(AsnEncodingRules.DER)) - { - writer.PushSequence(tag); - - this.Encode(writer); - - writer.PopSequence(tag); - - return writer.EncodeAsMemory(); - } - } - - public static KrbPaFxFastRequest Decode(ReadOnlyMemory data) - { - return Decode(data, AsnEncodingRules.DER); - } - - internal static KrbPaFxFastRequest Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet) - { - AsnReader reader = new AsnReader(encoded, ruleSet); - - Decode(reader, out KrbPaFxFastRequest decoded); - reader.ThrowIfNotEmpty(); - return decoded; - } - - internal static void Decode(AsnReader reader, out T decoded) - where T: KrbPaFxFastRequest, new() - { - if (reader == null) - { - throw new ArgumentNullException(nameof(reader)); - } - - decoded = new T(); - - Asn1Tag tag = reader.PeekTag(); - AsnReader explicitReader; - - if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) - { - explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); - KrbFastArmoredReq.Decode(explicitReader, out KrbFastArmoredReq tmpArmoredData); - decoded.ArmoredData = tmpArmoredData; - explicitReader.ThrowIfNotEmpty(); - } - else - { - throw new CryptographicException(); - } - } - } -} diff --git a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.xml b/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.xml deleted file mode 100644 index 849a3282..00000000 --- a/Kerberos.NET/Entities/PreAuthentication/KrbPaFxFastRequest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/SpNego/NegTokenInit.xml b/Kerberos.NET/Entities/SpNego/NegTokenInit.xml deleted file mode 100644 index f9b930ea..00000000 --- a/Kerberos.NET/Entities/SpNego/NegTokenInit.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/SpNego/NegTokenResp.xml b/Kerberos.NET/Entities/SpNego/NegTokenResp.xml deleted file mode 100644 index 7e74d727..00000000 --- a/Kerberos.NET/Entities/SpNego/NegTokenResp.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Entities/SpNego/NegotiationToken.xml b/Kerberos.NET/Entities/SpNego/NegotiationToken.xml deleted file mode 100644 index 1ee91b1d..00000000 --- a/Kerberos.NET/Entities/SpNego/NegotiationToken.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Kerberos.NET/Kerberos.NET.csproj b/Kerberos.NET/Kerberos.NET.csproj index 83d0999d..cafe2b33 100644 --- a/Kerberos.NET/Kerberos.NET.csproj +++ b/Kerberos.NET/Kerberos.NET.csproj @@ -37,37 +37,6 @@ ReferenceOutputAssembly="false" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -91,6 +60,5 @@ - diff --git a/Kerberos.NET/kerberos.asn b/Kerberos.NET/kerberos.asn index a5d4226c..0c295d22 100644 --- a/Kerberos.NET/kerberos.asn +++ b/Kerberos.NET/kerberos.asn @@ -696,4 +696,196 @@ TD-DH-PARAMETERS ::= SEQUENCE OF AlgorithmIdentifier -- Each AlgorithmIdentifier specifies a set of -- Diffie-Hellman domain parameters [IEEE1363]. -- This list is in decreasing preference order. +END + +-- PKIX types needed by PKINIT (from RFC 3280 / RFC 5280) +PKIX1Explicit88 { + iso (1) identified-organization (3) dod (6) internet (1) + security (5) mechanisms (5) pkix (7) id-mod (0) + id-pkix1-explicit (18) +} DEFINITIONS EXPLICIT TAGS ::= BEGIN + +AlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER, + parameters ANY OPTIONAL +} + +SubjectPublicKeyInfo ::= SEQUENCE { + algorithm AlgorithmIdentifier, + subjectPublicKey BIT STRING +} + +DomainParameters ::= SEQUENCE { + p ANY, + g ANY, + q ANY, + j ANY OPTIONAL, + validationParms ValidationParms OPTIONAL +} + +ValidationParms ::= SEQUENCE { + seed BIT STRING, + pgenCounter INTEGER +} + +END + +-- Kerberos Extensions (S4U, PAC, IAKERB, password change, KKDCP, etc.) +KerberosExtensions DEFINITIONS EXPLICIT TAGS ::= BEGIN + +IMPORTS + PrincipalName, Realm, KerberosString, Checksum, EncryptedData, EncryptionKey, + Int32, UInt32, Microseconds, KerberosTime, PA-DATA, KerberosFlags, + KDC-REQ-BODY, AuthorizationData + FROM KerberosV5Spec2 { iso(1) identified-organization(3) + dod(6) internet(1) security(5) kerberosV5(2) + modules(4) krb5spec2(2) }; + +-- RFC 3244 - Password Change +ChangePasswdData ::= SEQUENCE { + newpasswd [0] OCTET STRING, + targname [1] PrincipalName OPTIONAL, + targrealm [2] Realm OPTIONAL +} + +-- MS-KKDCP +KDC-PROXY-MESSAGE ::= SEQUENCE { + kerb-message [0] OCTET STRING, + target-domain [1] Realm OPTIONAL, + dclocator-hint [2] Int32 OPTIONAL +} + +-- RFC 6803 +EtypeList ::= SEQUENCE OF Int32 + +-- KERB-ERROR-DATA +KERB-ERROR-DATA ::= SEQUENCE { + data-type [1] Int32, + data-value [2] OCTET STRING +} + +-- MS-SFU PA-FOR-USER +PA-FOR-USER ::= SEQUENCE { + userName [0] PrincipalName, + userRealm [1] Realm, + cksum [2] Checksum, + auth-package [3] KerberosString +} + +-- MS-KILE PA-PAC-OPTIONS +PA-PAC-OPTIONS ::= SEQUENCE { + flags [0] KerberosFlags +} + +-- MS-KILE KERB-PA-PAC-REQUEST +KERB-PA-PAC-REQUEST ::= SEQUENCE { + include-pac [0] BOOLEAN +} + +-- MS-SFU PA-S4U-X509-USER +PA-S4U-X509-USER ::= SEQUENCE { + user-id [0] S4UUserID, + checksum [1] Checksum +} + +S4UUserID ::= SEQUENCE { + nonce [0] UInt32, + cname [1] PrincipalName OPTIONAL, + crealm [2] Realm, + subject-certificate [3] OCTET STRING OPTIONAL, + options [4] KerberosFlags OPTIONAL, + ... +} + +-- MS-KILE PA-SVR-REFERRAL-DATA +PA-SVR-REFERRAL-DATA ::= SEQUENCE { + referred-name [1] PrincipalName OPTIONAL, + referred-realm [0] Realm +} + +-- RFC 6784 IAKERB +IAKERB-HEADER ::= SEQUENCE { + target-realm [1] UTF8String, + cookie [2] OCTET STRING OPTIONAL, + header-flags [3] BIT STRING OPTIONAL, + ... +} + +END + +-- RFC 6113 FAST (Flexible Authentication Secure Tunneling) +KerberosV5-FAST { + iso(1) identified-organization(3) dod(6) internet(1) + security(5) kerberosV5(2) modules(4) fast(7) +} DEFINITIONS EXPLICIT TAGS ::= BEGIN + +IMPORTS + PrincipalName, Realm, Checksum, EncryptedData, EncryptionKey, + Int32, UInt32, Microseconds, KerberosTime, PA-DATA, + KerberosFlags, KDC-REQ-BODY + FROM KerberosV5Spec2 { iso(1) identified-organization(3) + dod(6) internet(1) security(5) kerberosV5(2) + modules(4) krb5spec2(2) }; + +KrbFastArmor ::= SEQUENCE { + armor-type [0] Int32, + armor-value [1] OCTET STRING OPTIONAL, + ... +} + +KrbFastArmoredReq ::= SEQUENCE { + armor [0] KrbFastArmor OPTIONAL, + req-checksum [1] Checksum, + enc-fast-req [2] EncryptedData, + ... +} + +KrbFastArmoredRep ::= SEQUENCE { + enc-fast-rep [0] EncryptedData, + ... +} + +KrbFastReq ::= SEQUENCE { + fast-options [0] KerberosFlags, + padata [1] SEQUENCE OF PA-DATA, + req-body [2] KDC-REQ-BODY, + ... +} + +KrbFastResponse ::= SEQUENCE { + padata [0] SEQUENCE OF PA-DATA, + strengthen-key [1] EncryptionKey OPTIONAL, + finished [2] KrbFastFinished OPTIONAL, + nonce [3] UInt32, + ... +} + +KrbFastFinished ::= SEQUENCE { + timestamp [0] KerberosTime, + usec [1] Microseconds, + crealm [2] Realm, + cname [3] PrincipalName, + ticket-checksum [4] Checksum, + ... +} + +PA-AUTHENTICATION-SET ::= SEQUENCE OF PA-AUTHENTICATION-SET-ELEM + +PA-AUTHENTICATION-SET-ELEM ::= SEQUENCE { + pa-type [0] Int32, + pa-hint [1] OCTET STRING OPTIONAL, + pa-value [2] OCTET STRING OPTIONAL, + ... +} + +PA-FX-FAST-REQUEST ::= CHOICE { + armored-data [0] KrbFastArmoredReq, + ... +} + +PA-FX-FAST-REPLY ::= CHOICE { + armored-data [0] KrbFastArmoredRep, + ... +} + END \ No newline at end of file diff --git a/Kerberos.NET/kerberos.asn.json b/Kerberos.NET/kerberos.asn.json index 86de6c3c..e56ac2e9 100644 --- a/Kerberos.NET/kerberos.asn.json +++ b/Kerberos.NET/kerberos.asn.json @@ -1,35 +1,63 @@ { + "Defaults": { + "TypePrefix": "Krb", + "Namespace": "Kerberos.NET.Entities", + "ExcludePrefix": ["NegotiationToken", "NegTokenInit", "NegTokenResp"] + }, "Types": { + // --- SPNEGO --- + "NegotiationToken": { + "Fields": { + "negTokenInit": { "CSharpName": "InitialToken" }, + "negTokenResp": { "CSharpName": "ResponseToken" } + } + }, + "NegTokenInit": { + "Fields": { + "mechTypes": { "CSharpName": "MechTypes", "BackingType": "Oid[]" }, + "reqFlags": { "CSharpName": "RequestFlags" }, + "mechToken": { "CSharpName": "MechToken" }, + "mechListMIC": { "CSharpName": "MechListMic" } + } + }, + "NegTokenResp": { + "Fields": { + "negState": { "CSharpName": "State", "EnumType": "NegotiateState", "TreatAsEnum": true }, + "supportedMech": { "CSharpName": "SupportedMech", "BackingType": "Oid" }, + "responseToken": { "CSharpName": "ResponseToken" }, + "mechListMIC": { "CSharpName": "MechListMic" } + } + }, + + // --- Kerberos V5 core (RFC 4120) --- "PrincipalName": { - "CSharpName": "KrbPrincipalName", "Fields": { "name-type": { "CSharpName": "Type", "EnumType": "PrincipalNameType", "TreatAsEnum": true }, "name-string": { "CSharpName": "Name", "BackingType": "string[]" } } }, "HostAddress": { - "CSharpName": "KrbHostAddress", "Fields": { "addr-type": { "CSharpName": "AddressType", "EnumType": "AddressType", "TreatAsEnum": true }, "address": { "CSharpName": "Address" } } }, "AuthorizationData": { - "CSharpName": "KrbAuthorizationData", + "EmitWrapper": true, + "WrapperClassName": "KrbAuthorizationDataSequence", + "WrapperProperty": "AuthorizationData", "Fields": { "ad-type": { "CSharpName": "Type", "EnumType": "AuthorizationDataType", "TreatAsEnum": true }, "ad-data": { "CSharpName": "Data" } } }, "PA-DATA": { - "CSharpName": "KrbPaData", "Fields": { "padata-type": { "CSharpName": "Type", "EnumType": "PaDataType", "TreatAsEnum": true }, "padata-value": { "CSharpName": "Value" } } }, "EncryptedData": { - "CSharpName": "KrbEncryptedData", "Fields": { "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, "kvno": { "CSharpName": "KeyVersionNumber" }, @@ -37,43 +65,35 @@ } }, "EncryptionKey": { - "CSharpName": "KrbEncryptionKey", "Fields": { "keytype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, "keyvalue": { "CSharpName": "KeyValue" } } }, "Checksum": { - "CSharpName": "KrbChecksum", "Fields": { "cksumtype": { "CSharpName": "Type", "EnumType": "ChecksumType", "TreatAsEnum": true }, "checksum": { "CSharpName": "Checksum" } } }, "TransitedEncoding": { - "CSharpName": "KrbTransitedEncoding", "Fields": { "tr-type": { "CSharpName": "Type", "EnumType": "TransitedEncodingType", "TreatAsEnum": true }, "contents": { "CSharpName": "Contents" } } }, "Ticket": { - "CSharpName": "KrbTicket", "Fields": { "tkt-vno": { "CSharpName": "TicketNumber" }, - "realm": { "CSharpName": "Realm" }, "sname": { "CSharpName": "SName" }, "enc-part": { "CSharpName": "EncryptedPart" } } }, "EncTicketPart": { - "CSharpName": "KrbEncTicketPart", "Fields": { "flags": { "CSharpName": "Flags", "EnumType": "TicketFlags", "TreatAsEnum": true }, - "key": { "CSharpName": "Key" }, "crealm": { "CSharpName": "CRealm" }, "cname": { "CSharpName": "CName" }, - "transited": { "CSharpName": "Transited" }, "authtime": { "CSharpName": "AuthTime" }, "starttime": { "CSharpName": "StartTime" }, "endtime": { "CSharpName": "EndTime" }, @@ -83,7 +103,6 @@ } }, "KDC-REQ": { - "CSharpName": "KrbKdcReq", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, @@ -92,50 +111,30 @@ } }, "KDC-REQ-BODY": { - "CSharpName": "KrbKdcReqBody", "Fields": { "kdc-options": { "CSharpName": "KdcOptions", "EnumType": "KdcOptions", "TreatAsEnum": true }, "cname": { "CSharpName": "CName" }, - "realm": { "CSharpName": "Realm" }, "sname": { "CSharpName": "SName" }, - "from": { "CSharpName": "From" }, - "till": { "CSharpName": "Till" }, "rtime": { "CSharpName": "RTime" }, - "nonce": { "CSharpName": "Nonce" }, "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, - "addresses": { "CSharpName": "Addresses" }, "enc-authorization-data": { "CSharpName": "EncAuthorizationData" }, "additional-tickets": { "CSharpName": "AdditionalTickets" } } }, - "AS-REQ": { - "CSharpName": "KrbAsReq", - "InheritsFrom": "KrbKdcReq" - }, - "TGS-REQ": { - "CSharpName": "KrbTgsReq", - "InheritsFrom": "KrbKdcReq" - }, + "AS-REQ": { "InheritsFrom": "KrbKdcReq" }, + "TGS-REQ": { "InheritsFrom": "KrbKdcReq" }, "KDC-REP": { - "CSharpName": "KrbKdcRep", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, "padata": { "CSharpName": "PaData" }, "crealm": { "CSharpName": "CRealm" }, "cname": { "CSharpName": "CName" }, - "ticket": { "CSharpName": "Ticket" }, "enc-part": { "CSharpName": "EncPart" } } }, - "AS-REP": { - "CSharpName": "KrbAsRep", - "InheritsFrom": "KrbKdcRep" - }, - "TGS-REP": { - "CSharpName": "KrbTgsRep", - "InheritsFrom": "KrbKdcRep" - }, + "AS-REP": { "InheritsFrom": "KrbKdcRep" }, + "TGS-REP": { "InheritsFrom": "KrbKdcRep" }, "EncASRepPart": { "CSharpName": "KrbEncAsRepPart", "InheritsFrom": "KrbEncKdcRepPart" @@ -147,9 +146,7 @@ "EncKDCRepPart": { "CSharpName": "KrbEncKdcRepPart", "Fields": { - "key": { "CSharpName": "Key" }, "last-req": { "CSharpName": "LastReq" }, - "nonce": { "CSharpName": "Nonce" }, "key-expiration": { "CSharpName": "KeyExpiration" }, "flags": { "CSharpName": "Flags", "EnumType": "TicketFlags", "TreatAsEnum": true }, "authtime": { "CSharpName": "AuthTime" }, @@ -163,24 +160,19 @@ } }, "LastReq": { - "CSharpName": "KrbLastReq", "Fields": { "lr-type": { "CSharpName": "Type" }, "lr-value": { "CSharpName": "Value" } } }, "AP-REQ": { - "CSharpName": "KrbApReq", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, - "ap-options": { "CSharpName": "ApOptions", "EnumType": "ApOptions", "TreatAsEnum": true }, - "ticket": { "CSharpName": "Ticket" }, - "authenticator": { "CSharpName": "Authenticator" } + "ap-options": { "CSharpName": "ApOptions", "EnumType": "ApOptions", "TreatAsEnum": true } } }, "Authenticator": { - "CSharpName": "KrbAuthenticator", "Fields": { "authenticator-vno": { "CSharpName": "AuthenticatorVersionNumber" }, "crealm": { "CSharpName": "CRealm" }, @@ -188,13 +180,11 @@ "cksum": { "CSharpName": "Checksum" }, "cusec": { "CSharpName": "CuSec" }, "ctime": { "CSharpName": "CTime" }, - "subkey": { "CSharpName": "Subkey" }, "seq-number": { "CSharpName": "SequenceNumber" }, "authorization-data": { "CSharpName": "AuthorizationData" } } }, "AP-REP": { - "CSharpName": "KrbApRep", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, @@ -211,7 +201,6 @@ } }, "KRB-SAFE": { - "CSharpName": "KrbSafe", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, @@ -220,18 +209,14 @@ } }, "KRB-SAFE-BODY": { - "CSharpName": "KrbSafeBody", "Fields": { "user-data": { "CSharpName": "UserData" }, - "timestamp": { "CSharpName": "Timestamp" }, - "usec": { "CSharpName": "Usec" }, "seq-number": { "CSharpName": "SeqNumber" }, "s-address": { "CSharpName": "SAddress" }, "r-address": { "CSharpName": "RAddress" } } }, "KRB-PRIV": { - "CSharpName": "KrbPriv", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, @@ -239,40 +224,30 @@ } }, "EncKrbPrivPart": { - "CSharpName": "KrbEncKrbPrivPart", "Fields": { "user-data": { "CSharpName": "UserData" }, - "timestamp": { "CSharpName": "Timestamp" }, - "usec": { "CSharpName": "Usec" }, "seq-number": { "CSharpName": "SeqNumber" }, "s-address": { "CSharpName": "SAddress" }, "r-address": { "CSharpName": "RAddress" } } }, "KRB-CRED": { - "CSharpName": "KrbCred", "Fields": { "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, - "tickets": { "CSharpName": "Tickets" }, "enc-part": { "CSharpName": "EncryptedPart" } } }, "EncKrbCredPart": { - "CSharpName": "KrbEncKrbCredPart", "Fields": { "ticket-info": { "CSharpName": "TicketInfo" }, - "nonce": { "CSharpName": "Nonce" }, - "timestamp": { "CSharpName": "Timestamp" }, "usec": { "CSharpName": "USec" }, "s-address": { "CSharpName": "SAddress" }, "r-address": { "CSharpName": "RAddress" } } }, "KrbCredInfo": { - "CSharpName": "KrbCredInfo", "Fields": { - "key": { "CSharpName": "Key" }, "prealm": { "CSharpName": "Realm" }, "pname": { "CSharpName": "PName" }, "flags": { "CSharpName": "Flags", "EnumType": "TicketFlags", "TreatAsEnum": true }, @@ -291,27 +266,23 @@ "pvno": { "CSharpName": "ProtocolVersionNumber" }, "msg-type": { "CSharpName": "MessageType", "EnumType": "MessageType", "TreatAsEnum": true }, "ctime": { "CSharpName": "CTime" }, - "cusec": { "CSharpName": "Cusec" }, "stime": { "CSharpName": "STime" }, "susec": { "CSharpName": "Susc" }, "error-code": { "CSharpName": "ErrorCode", "EnumType": "KerberosErrorCode", "TreatAsEnum": true }, "crealm": { "CSharpName": "CRealm" }, "cname": { "CSharpName": "CName" }, - "realm": { "CSharpName": "Realm" }, "sname": { "CSharpName": "SName" }, "e-text": { "CSharpName": "EText" }, "e-data": { "CSharpName": "EData" } } }, "TYPED-DATA": { - "CSharpName": "KrbTypedData", "Fields": { "data-type": { "CSharpName": "Type" }, "data-value": { "CSharpName": "Value" } } }, "PA-ENC-TS-ENC": { - "CSharpName": "KrbPaEncTsEnc", "Fields": { "patimestamp": { "CSharpName": "PaTimestamp" }, "pausec": { "CSharpName": "PaUSec" } @@ -320,80 +291,37 @@ "ETYPE-INFO-ENTRY": { "CSharpName": "KrbETypeInfoEntry", "Fields": { - "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, - "salt": { "CSharpName": "Salt" } + "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true } } }, "ETYPE-INFO2-ENTRY": { "CSharpName": "KrbETypeInfo2Entry", "Fields": { "etype": { "CSharpName": "EType", "EnumType": "EncryptionType", "TreatAsEnum": true }, - "salt": { "CSharpName": "Salt" }, "s2kparams": { "CSharpName": "S2kParams" } } }, "AD-KDCIssued": { - "CSharpName": "KrbAdKdcIssued", "Fields": { - "ad-checksum": { "CSharpName": "Checksum" }, - "i-realm": { "CSharpName": "IRealm" }, - "i-sname": { "CSharpName": "ISName" }, - "elements": { "CSharpName": "Elements" } + "ad-checksum": { "CSharpName": "Checksum" } } }, "AD-AND-OR": { - "CSharpName": "KrbAdAndOr", - "Fields": { - "condition-count": { "CSharpName": "ConditionCount" }, - "elements": { "CSharpName": "Elements" } - } - }, - "NegotiationToken": { - "CSharpName": "NegotiationToken", - "Namespace": "Kerberos.NET.Entities", - "Fields": { - "negTokenInit": { "CSharpName": "InitialToken" }, - "negTokenResp": { "CSharpName": "ResponseToken" } - } - }, - "NegTokenInit": { - "CSharpName": "NegTokenInit", - "Namespace": "Kerberos.NET.Entities", - "Fields": { - "mechTypes": { "CSharpName": "MechTypes", "BackingType": "Oid[]" }, - "reqFlags": { "CSharpName": "RequestFlags" }, - "mechToken": { "CSharpName": "MechToken" }, - "mechListMIC": { "CSharpName": "MechListMic" } - } - }, - "NegTokenResp": { - "CSharpName": "NegTokenResp", - "Namespace": "Kerberos.NET.Entities", "Fields": { - "negState": { "CSharpName": "State", "EnumType": "NegotiateState", "TreatAsEnum": true }, - "supportedMech": { "CSharpName": "SupportedMech", "BackingType": "Oid" }, - "responseToken": { "CSharpName": "ResponseToken" }, - "mechListMIC": { "CSharpName": "MechListMic" } + "condition-count": { "CSharpName": "ConditionCount" } } }, + + // --- PKINIT (RFC 4556) --- "PA-PK-AS-REQ": { - "CSharpName": "KrbPaPkAsReq", "Fields": { "signedAuthPack": { "CSharpName": "SignedAuthPack" }, "trustedCertifiers": { "CSharpName": "TrustedCertifiers" }, "kdcPkId": { "CSharpName": "KdcPkId" } } }, - "ExternalPrincipalIdentifier": { - "CSharpName": "KrbExternalPrincipalIdentifier", - "Fields": { - "subjectName": { "CSharpName": "SubjectName" }, - "issuerAndSerialNumber": { "CSharpName": "IssuerAndSerialNumber" }, - "subjectKeyIdentifier": { "CSharpName": "SubjectKeyIdentifier" } - } - }, + "ExternalPrincipalIdentifier": {}, "AuthPack": { - "CSharpName": "KrbAuthPack", "Fields": { "pkAuthenticator": { "CSharpName": "PKAuthenticator" }, "clientPublicValue": { "CSharpName": "ClientPublicValue" }, @@ -402,23 +330,15 @@ } }, "PKAuthenticator": { - "CSharpName": "KrbPKAuthenticator", "Fields": { "cusec": { "CSharpName": "CuSec" }, - "ctime": { "CSharpName": "CTime" }, - "nonce": { "CSharpName": "Nonce" }, - "paChecksum": { "CSharpName": "PaChecksum" } + "ctime": { "CSharpName": "CTime" } } }, "KRB5PrincipalName": { - "CSharpName": "KrbPrincipal5Name", - "Fields": { - "realm": { "CSharpName": "Realm" }, - "principalName": { "CSharpName": "PrincipalName" } - } + "CSharpName": "KrbPrincipal5Name" }, "PA-PK-AS-REP": { - "CSharpName": "KrbPaPkAsRep", "Fields": { "dhInfo": { "CSharpName": "DHInfo" }, "encKeyPack": { "CSharpName": "EncKeyPack" } @@ -434,23 +354,177 @@ "KDCDHKeyInfo": { "CSharpName": "KrbKdcDHKeyInfo", "Fields": { - "subjectPublicKey": { "CSharpName": "SubjectPublicKey" }, - "nonce": { "CSharpName": "Nonce" }, "dhKeyExpiration": { "CSharpName": "DHKeyExpiration" } } }, - "ReplyKeyPack": { - "CSharpName": "KrbReplyKeyPack", + "ReplyKeyPack": {}, + + // --- PKIX types --- + "SubjectPublicKeyInfo": {}, + "AlgorithmIdentifier": {}, + "DomainParameters": { + "CSharpName": "KrbDiffieHellmanDomainParameters", + "Fields": { + "validationParms": { "CSharpName": "ValidationParameters" } + } + }, + "ValidationParms": { + "CSharpName": "KrbDiffieHellmanValidationParameters", + "Fields": { + "pgenCounter": { "CSharpName": "PGenOutput" } + } + }, + + // --- Kerberos Extensions --- + "ChangePasswdData": { + "Fields": { + "newpasswd": { "CSharpName": "NewPasswd" }, + "targname": { "CSharpName": "TargName" }, + "targrealm": { "CSharpName": "TargRealm" } + } + }, + "KDC-PROXY-MESSAGE": { + "CSharpName": "KdcProxyMessage", "Fields": { - "replyKey": { "CSharpName": "ReplyKey" }, - "asChecksum": { "CSharpName": "AsChecksum" } + "kerb-message": { "CSharpName": "KerbMessage" }, + "target-domain": { "CSharpName": "TargetDomain" }, + "dclocator-hint": { "CSharpName": "DcLocatorHint", "EnumType": "DcLocatorHint", "TreatAsEnum": true } } }, - "SubjectPublicKeyInfo": { - "CSharpName": "KrbSubjectPublicKeyInfo" + "EtypeList": { + "CSharpName": "KrbETypeList", + "EmitWrapper": true, + "WrapperProperty": "List", + "Fields": { + "_element": { "EnumType": "EncryptionType", "TreatAsEnum": true } + } + }, + "KERB-ERROR-DATA": { + "CSharpName": "KrbErrorData", + "Fields": { + "data-type": { "CSharpName": "Type", "EnumType": "KrbErrorDataType", "TreatAsEnum": true }, + "data-value": { "CSharpName": "Value" } + } + }, + "METHOD-DATA": { + "CSharpName": "KrbMethodData", + "EmitWrapper": true, + "WrapperProperty": "MethodData" + }, + "ETYPE-INFO2": { + "CSharpName": "KrbETypeInfo2", + "EmitWrapper": true, + "WrapperProperty": "ETypeInfo" + }, + "PA-FOR-USER": { + "Fields": { + "userName": { "CSharpName": "UserName" }, + "userRealm": { "CSharpName": "UserRealm" }, + "cksum": { "CSharpName": "Checksum" }, + "auth-package": { "CSharpName": "AuthPackage" } + } + }, + "PA-PAC-OPTIONS": { + "Fields": { + "flags": { "CSharpName": "Flags", "EnumType": "PacOptions", "TreatAsEnum": true } + } + }, + "KERB-PA-PAC-REQUEST": { + "CSharpName": "KrbPaPacRequest", + "Fields": { + "include-pac": { "CSharpName": "IncludePac" } + } + }, + "PA-S4U-X509-USER": { + "Fields": { + "user-id": { "CSharpName": "UserId" }, + "checksum": { "CSharpName": "Checksum" } + } + }, + "S4UUserID": { + "CSharpName": "KrbS4uUserId", + "Fields": { + "cname": { "CSharpName": "CName" }, + "crealm": { "CSharpName": "Realm" }, + "subject-certificate": { "CSharpName": "SubjectCertificate" }, + "options": { "CSharpName": "Options", "EnumType": "S4uOptions", "TreatAsEnum": true } + } + }, + "PA-SVR-REFERRAL-DATA": { + "Fields": { + "referred-name": { "CSharpName": "ReferredName" }, + "referred-realm": { "CSharpName": "ReferredRealm" } + } + }, + "IAKERB-HEADER": { + "CSharpName": "IAKerbHeader", + "Fields": { + "target-realm": { "CSharpName": "TargetRealm" }, + "header-flags": { "CSharpName": "HeaderFlags" } + } }, - "AlgorithmIdentifier": { - "CSharpName": "KrbAlgorithmIdentifier" + + // --- FAST (RFC 6113) --- + "KrbFastArmor": { + "Fields": { + "armor-type": { "CSharpName": "Type", "EnumType": "KrbArmorType", "TreatAsEnum": true }, + "armor-value": { "CSharpName": "Value" } + } + }, + "KrbFastArmoredReq": { + "Fields": { + "req-checksum": { "CSharpName": "RequestChecksum" }, + "enc-fast-req": { "CSharpName": "EncryptedFastRequest" } + } + }, + "KrbFastArmoredRep": { + "Fields": { + "enc-fast-rep": { "CSharpName": "EncFastRep" } + } + }, + "KrbFastReq": { + "Fields": { + "fast-options": { "CSharpName": "FastOptions", "EnumType": "FastOptions", "TreatAsEnum": true }, + "padata": { "CSharpName": "PaData" }, + "req-body": { "CSharpName": "ReqBody" } + } + }, + "KrbFastResponse": { + "Fields": { + "padata": { "CSharpName": "PaData" }, + "strengthen-key": { "CSharpName": "StrengthenKey" } + } + }, + "KrbFastFinished": { + "Fields": { + "usec": { "CSharpName": "USec" }, + "crealm": { "CSharpName": "CRealm" }, + "cname": { "CSharpName": "CName" }, + "ticket-checksum": { "CSharpName": "TicketChecksum" } + } + }, + "PA-AUTHENTICATION-SET": { + "CSharpName": "KrbPaAuthenticationSet", + "EmitWrapper": true, + "WrapperProperty": "PaAuthenticationSet" + }, + "PA-AUTHENTICATION-SET-ELEM": { + "CSharpName": "KrbPaAuthenticationSetElement", + "Fields": { + "pa-type": { "CSharpName": "Type", "EnumType": "PaDataType", "TreatAsEnum": true }, + "pa-hint": { "CSharpName": "Hint" }, + "pa-value": { "CSharpName": "Value" } + } + }, + "PA-FX-FAST-REQUEST": { + "Fields": { + "armored-data": { "CSharpName": "ArmoredData" } + } + }, + "PA-FX-FAST-REPLY": { + "Fields": { + "armored-data": { "CSharpName": "ArmoredData" } + } } } } diff --git a/Tools/Asn1CodeGen/CSharpCodeEmitter.cs b/Tools/Asn1CodeGen/CSharpCodeEmitter.cs index 56a0041d..5c197827 100644 --- a/Tools/Asn1CodeGen/CSharpCodeEmitter.cs +++ b/Tools/Asn1CodeGen/CSharpCodeEmitter.cs @@ -14,9 +14,29 @@ namespace Kerberos.NET.Asn1CodeGen public class TypeConfig { public string CSharpName { get; set; } - public string Namespace { get; set; } = "Kerberos.NET.Entities"; + public string Namespace { get; set; } public int? ApplicationTag { get; set; } public string InheritsFrom { get; set; } + + /// + /// When true, emits a wrapper class for a SEQUENCE OF alias type. + /// The wrapper class has a single array property with Encode/Decode methods. + /// e.g. METHOD-DATA ::= SEQUENCE OF PA-DATA → class KrbMethodData { KrbPaData[] MethodData; } + /// + public bool EmitWrapper { get; set; } + + /// + /// Name of the wrapper property for SEQUENCE OF wrapper types. + /// If not set, defaults to the C# class name. + /// + public string WrapperProperty { get; set; } + + /// + /// Class name for the wrapper when the base type is a SEQUENCE OF inline SEQUENCE. + /// Only used for TypeAssignment wrappers where the inner class has a different name. + /// + public string WrapperClassName { get; set; } + public Dictionary Fields { get; set; } = new(); } @@ -28,8 +48,29 @@ public class FieldConfig public bool TreatAsEnum { get; set; } } + public class DefaultsConfig + { + /// + /// Prefix to prepend to PascalCased ASN.1 type names (e.g. "Krb" → "KrbTicket"). + /// Only applied when no explicit CSharpName is specified in Types config. + /// + public string TypePrefix { get; set; } + + /// + /// Default namespace for generated types. + /// + public string Namespace { get; set; } = "Kerberos.NET.Entities"; + + /// + /// ASN.1 type names that should NOT get the TypePrefix applied. + /// Useful for types like NegotiationToken that don't follow the common prefix convention. + /// + public List ExcludePrefix { get; set; } = new(); + } + public class EmitterConfig { + public DefaultsConfig Defaults { get; set; } = new DefaultsConfig(); public Dictionary Types { get; set; } = new(); } @@ -100,7 +141,7 @@ public Dictionary EmitAll() foreach (var assignment in module.TypeAssignments) { var tc = this.GetTypeConfig(assignment.Name); - var className = tc.CSharpName ?? ToPascalCase(assignment.Name); + var className = tc.CSharpName ?? this.ApplyTypeNameConvention(assignment.Name); // Handle SEQUENCE OF with inline SEQUENCE: // e.g. AuthorizationData ::= SEQUENCE OF SEQUENCE { ... } @@ -118,6 +159,31 @@ public Dictionary EmitAll() var code = this.EmitTypeAssignment(innerAssignment, module); files[className + ".generated.cs"] = code; + + // Also emit a wrapper class if configured + if (tc.EmitWrapper && tc.WrapperClassName != null) + { + var wrapperClassName = tc.WrapperClassName; + var wrapperPropName = tc.WrapperProperty ?? ToPascalCase(assignment.Name); + + // Create a synthetic SEQUENCE OF with the resolved element reference + var wrapperSeqOf = new Asn1SequenceOfType + { + ElementType = new Asn1ReferencedType { ReferenceName = assignment.Name } + }; + + var wrapperTc = new TypeConfig + { + CSharpName = wrapperClassName, + Namespace = tc.Namespace, + EmitWrapper = true, + WrapperProperty = wrapperPropName, + }; + + var wrapperCode = this.EmitSequenceOfWrapper( + assignment.Name, wrapperSeqOf, wrapperTc, wrapperClassName, module); + files[wrapperClassName + ".generated.cs"] = wrapperCode; + } } else { @@ -125,6 +191,24 @@ public Dictionary EmitAll() files[className + ".generated.cs"] = code; } } + + // Emit wrapper classes for SEQUENCE OF aliases that have EmitWrapper=true + foreach (var kvp in module.TypeAliases) + { + var tc = this.GetTypeConfig(kvp.Key); + + if (!tc.EmitWrapper) + { + continue; + } + + if (kvp.Value is Asn1SequenceOfType seqOfAlias) + { + var className = tc.CSharpName ?? this.ApplyTypeNameConvention(kvp.Key); + var code = this.EmitSequenceOfWrapper(kvp.Key, seqOfAlias, tc, className, module); + files[className + ".generated.cs"] = code; + } + } } return files; @@ -1034,23 +1118,35 @@ private void EmitFieldDecode(StringBuilder sb, Asn1Field field, TypeConfig tc, s if (field.Optional) { - sb.AppendLine($" if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, {tagNum})))"); - sb.AppendLine(" {"); - - if (hasTag && isImplicit && this.IsPrimitiveType(resolved)) + if (hasTag) { - this.EmitImplicitPrimitiveDecode(sb, resolved, propName, fc, tagNum, " "); + sb.AppendLine($" if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, {tagNum})))"); + sb.AppendLine(" {"); + + if (isImplicit && this.IsPrimitiveType(resolved)) + { + this.EmitImplicitPrimitiveDecode(sb, resolved, propName, fc, tagNum, " "); + } + else + { + sb.AppendLine($" explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum})); "); + sb.AppendLine(" "); + this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", true); + sb.AppendLine(" explicitReader.ThrowIfNotEmpty();"); + } + + sb.AppendLine(" }"); + sb.AppendLine(); } - else if (hasTag) + else { - sb.AppendLine($" explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, {tagNum})); "); - sb.AppendLine(" "); - this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", true); - sb.AppendLine(" explicitReader.ThrowIfNotEmpty();"); + // Optional untagged field - check if there's data remaining + sb.AppendLine(" if (sequenceReader.HasData)"); + sb.AppendLine(" {"); + this.EmitFieldDecodeValue(sb, resolved, propName, fc, " ", true, "sequenceReader"); + sb.AppendLine(" }"); + sb.AppendLine(); } - - sb.AppendLine(" }"); - sb.AppendLine(); } else { @@ -1142,14 +1238,14 @@ private void EmitFieldDecodeValue(StringBuilder sb, Asn1Type type, string propNa if (type is Asn1IntegerType) { - string csharpType = (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) - ? fc.EnumType : "int"; + bool isEnum = fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType); + string castPrefix = isEnum ? $"({fc.EnumType})" : ""; if (optional) { - sb.AppendLine($"{indent}if ({reader}.TryReadInt32(out {csharpType} tmp{propName}))"); + sb.AppendLine($"{indent}if ({reader}.TryReadInt32(out int tmp{propName}))"); sb.AppendLine($"{indent}{{"); - sb.AppendLine($"{indent} decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent} decoded.{propName} = {castPrefix}tmp{propName};"); sb.AppendLine($"{indent}}}"); sb.AppendLine($"{indent}else"); sb.AppendLine($"{indent}{{"); @@ -1158,12 +1254,12 @@ private void EmitFieldDecodeValue(StringBuilder sb, Asn1Type type, string propNa } else { - sb.AppendLine($"{indent}if (!{reader}.TryReadInt32(out {csharpType} tmp{propName}))"); + sb.AppendLine($"{indent}if (!{reader}.TryReadInt32(out int tmp{propName}))"); sb.AppendLine($"{indent}{{"); sb.AppendLine($"{indent} {reader}.ThrowIfNotEmpty();"); sb.AppendLine($"{indent}}}"); sb.AppendLine($"{indent}"); - sb.AppendLine($"{indent}decoded.{propName} = tmp{propName};"); + sb.AppendLine($"{indent}decoded.{propName} = {castPrefix}tmp{propName};"); } } else if (type is Asn1OctetStringType) @@ -1333,16 +1429,322 @@ private void EmitSequenceOfElementDecode(StringBuilder sb, Asn1Type elementType, #endregion + #region SEQUENCE OF Wrapper + + /// + /// Emits a wrapper class for a SEQUENCE OF alias type. + /// e.g. METHOD-DATA ::= SEQUENCE OF PA-DATA generates: + /// class KrbMethodData { KrbPaData[] MethodData { get; set; } ... } + /// + private string EmitSequenceOfWrapper( + string asnName, + Asn1SequenceOfType seqOfType, + TypeConfig tc, + string className, + Asn1Module module) + { + var ns = tc.Namespace; + var elemType = seqOfType.ElementType; + var resolvedElem = this.ResolveFieldType(elemType); + string elemTypeName; + + // Determine the element's C# type name based on resolved type + if (resolvedElem is Asn1IntegerType) + { + var fc = tc.Fields.Count > 0 ? tc.Fields.Values.First() : new FieldConfig(); + + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + elemTypeName = fc.EnumType; + } + else + { + elemTypeName = "int"; + } + } + else if (resolvedElem is Asn1OctetStringType) + { + elemTypeName = "ReadOnlyMemory"; + } + else if (elemType is Asn1ReferencedType refType && this.IsGeneratableReference(refType.ReferenceName)) + { + elemTypeName = this.ResolveCSharpTypeName(refType.ReferenceName); + } + else + { + elemTypeName = "ReadOnlyMemory"; + } + + // Property name: use WrapperProperty config or fall back to class name + string propName = tc.WrapperProperty ?? className; + + var usings = new HashSet + { + "System", + "System.Collections.Generic", + "System.Runtime.InteropServices", + "System.Security.Cryptography", + "System.Security.Cryptography.Asn1", + "Kerberos.NET.Crypto", + "Kerberos.NET.Asn1", + }; + + var sb = new StringBuilder(); + this.EmitHeader(sb); + + foreach (var u in usings.OrderBy(x => x)) + { + sb.AppendLine($"using {u};"); + } + + sb.AppendLine(); + sb.AppendLine($"namespace {ns}"); + sb.AppendLine("{"); + sb.AppendLine($" public partial class {className}"); + sb.AppendLine(" {"); + sb.AppendLine(" /*"); + sb.AppendLine($" {asnName} ::= SEQUENCE OF {this.GetAsnTypeName(elemType)}"); + sb.AppendLine(" */"); + sb.AppendLine(" "); + sb.AppendLine($" public {elemTypeName}[] {propName} {{ get; set; }}"); + sb.AppendLine(" "); + + // DEBUG tag validation + sb.AppendLine("#if DEBUG"); + sb.AppendLine($" static {className}()"); + sb.AppendLine(" {"); + sb.AppendLine(" var usedTags = new System.Collections.Generic.Dictionary();"); + sb.AppendLine(" Action ensureUniqueTag = (tag, fieldName) =>"); + sb.AppendLine(" {"); + sb.AppendLine(" if (usedTags.TryGetValue(tag, out string existing))"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new InvalidOperationException($\"Tag '{tag}' is in use by both '{existing}' and '{fieldName}'\");"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" usedTags.Add(tag, fieldName);"); + sb.AppendLine(" };"); + sb.AppendLine(" "); + sb.AppendLine($" ensureUniqueTag(Asn1Tag.Sequence, \"{propName}\");"); + sb.AppendLine(" }"); + sb.AppendLine("#endif"); + + // Encode + sb.AppendLine(" // Encoding methods"); + sb.AppendLine(" public ReadOnlyMemory Encode()"); + sb.AppendLine(" {"); + sb.AppendLine(" var writer = new AsnWriter(AsnEncodingRules.DER);"); + sb.AppendLine(); + sb.AppendLine(" Encode(writer);"); + sb.AppendLine(); + sb.AppendLine(" return writer.EncodeAsMemory();"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" internal void Encode(AsnWriter writer)"); + sb.AppendLine(" {"); + sb.AppendLine(" bool wroteValue = false; "); + sb.AppendLine(" "); + sb.AppendLine($" if ({propName} != null)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (wroteValue)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new CryptographicException();"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + sb.AppendLine(" writer.PushSequence();"); + sb.AppendLine(" "); + sb.AppendLine($" for (int i = 0; i < {propName}.Length; i++)"); + sb.AppendLine(" {"); + this.EmitSequenceOfWrapperElementEncode(sb, resolvedElem, propName, tc); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" writer.PopSequence();"); + sb.AppendLine(); + sb.AppendLine(" wroteValue = true;"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" if (!wroteValue)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new CryptographicException();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + + // EncodeApplication + sb.AppendLine(" "); + sb.AppendLine(" internal ReadOnlyMemory EncodeApplication(Asn1Tag tag)"); + sb.AppendLine(" {"); + sb.AppendLine(" using (var writer = new AsnWriter(AsnEncodingRules.DER))"); + sb.AppendLine(" {"); + sb.AppendLine(" writer.PushSequence(tag);"); + sb.AppendLine(" "); + sb.AppendLine(" this.Encode(writer);"); + sb.AppendLine(); + sb.AppendLine(" writer.PopSequence(tag);"); + sb.AppendLine(); + sb.AppendLine(" return writer.EncodeAsMemory();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(" "); + + // Decode + sb.AppendLine($" public static {className} Decode(ReadOnlyMemory data)"); + sb.AppendLine(" {"); + sb.AppendLine(" return Decode(data, AsnEncodingRules.DER);"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" internal static {className} Decode(ReadOnlyMemory encoded, AsnEncodingRules ruleSet)"); + sb.AppendLine(" {"); + sb.AppendLine(" AsnReader reader = new AsnReader(encoded, ruleSet);"); + sb.AppendLine(" "); + sb.AppendLine($" Decode(reader, out {className} decoded);"); + sb.AppendLine(" reader.ThrowIfNotEmpty();"); + sb.AppendLine(" return decoded;"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" internal static void Decode(AsnReader reader, out T decoded)"); + sb.AppendLine($" where T: {className}, new()"); + sb.AppendLine(" {"); + sb.AppendLine(" if (reader == null)"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new ArgumentNullException(nameof(reader));"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" decoded = new T();"); + sb.AppendLine(" "); + sb.AppendLine(" Asn1Tag tag = reader.PeekTag();"); + sb.AppendLine(" AsnReader collectionReader;"); + sb.AppendLine(" "); + sb.AppendLine(" if (tag.HasSameClassAndValue(Asn1Tag.Sequence))"); + sb.AppendLine(" {"); + sb.AppendLine(" // Decode SEQUENCE OF for " + propName); + sb.AppendLine(" {"); + sb.AppendLine(" collectionReader = reader.ReadSequence();"); + + this.EmitSequenceOfWrapperElementDecode(sb, resolvedElem, propName, elemTypeName, tc); + + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(" else"); + sb.AppendLine(" {"); + sb.AppendLine(" throw new CryptographicException();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine("}"); + return sb.ToString(); + } + + private void EmitSequenceOfWrapperElementEncode(StringBuilder sb, Asn1Type resolvedElem, string propName, TypeConfig tc) + { + if (resolvedElem is Asn1IntegerType) + { + var fc = tc.Fields.Count > 0 ? tc.Fields.Values.First() : new FieldConfig(); + + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($" writer.WriteInteger((long){propName}[i]); "); + } + else + { + sb.AppendLine($" writer.WriteInteger({propName}[i]); "); + } + } + else + { + sb.AppendLine($" {propName}[i]?.Encode(writer); "); + } + } + + private void EmitSequenceOfWrapperElementDecode(StringBuilder sb, Asn1Type resolvedElem, string propName, string elemTypeName, TypeConfig tc) + { + if (resolvedElem is Asn1IntegerType) + { + var fc = tc.Fields.Count > 0 ? tc.Fields.Values.First() : new FieldConfig(); + + if (fc.TreatAsEnum && !string.IsNullOrEmpty(fc.EnumType)) + { + sb.AppendLine($" var tmpList = new List<{fc.EnumType}>();"); + sb.AppendLine(); + sb.AppendLine(" while (collectionReader.HasData)"); + sb.AppendLine(" {"); + sb.AppendLine($" if (!collectionReader.TryReadInt32<{fc.EnumType}>(out {fc.EnumType} tmp))"); + sb.AppendLine(" {"); + sb.AppendLine(" break;"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" tmpList.Add(tmp);"); + sb.AppendLine(" }"); + } + else + { + sb.AppendLine(" var tmpList = new List();"); + sb.AppendLine(); + sb.AppendLine(" while (collectionReader.HasData)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (!collectionReader.TryReadInt32(out int tmp))"); + sb.AppendLine(" {"); + sb.AppendLine(" break;"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" tmpList.Add(tmp);"); + sb.AppendLine(" }"); + } + } + else + { + sb.AppendLine($" var tmpList = new List<{elemTypeName}>();"); + sb.AppendLine($" {elemTypeName} tmpItem;"); + sb.AppendLine(); + sb.AppendLine(" while (collectionReader.HasData)"); + sb.AppendLine(" {"); + sb.AppendLine($" {elemTypeName}.Decode<{elemTypeName}>(collectionReader, out {elemTypeName} tmp);"); + sb.AppendLine($" tmpItem = tmp; "); + sb.AppendLine(" tmpList.Add(tmpItem);"); + sb.AppendLine(" }"); + } + + sb.AppendLine(); + sb.AppendLine($" decoded.{propName} = tmpList.ToArray();"); + } + + private string GetAsnTypeName(Asn1Type type) + { + if (type is Asn1ReferencedType refType) return refType.ReferenceName; + if (type is Asn1IntegerType) return "INTEGER"; + if (type is Asn1OctetStringType) return "OCTET STRING"; + if (type is Asn1BitStringType) return "BIT STRING"; + if (type is Asn1BooleanType) return "BOOLEAN"; + if (type is Asn1ObjectIdentifierType) return "OBJECT IDENTIFIER"; + return "ANY"; + } + + #endregion + #region Helpers private TypeConfig GetTypeConfig(string name) { + string defaultNs = this.config.Defaults.Namespace ?? "Kerberos.NET.Entities"; + if (this.config.Types.TryGetValue(name, out var tc)) { + if (tc.Namespace == null) + { + tc.Namespace = defaultNs; + } + + if (tc.CSharpName == null) + { + tc.CSharpName = this.ApplyTypeNameConvention(name); + } + return tc; } - return new TypeConfig { CSharpName = ToPascalCase(name) }; + return new TypeConfig + { + CSharpName = this.ApplyTypeNameConvention(name), + Namespace = defaultNs + }; } private FieldConfig GetFieldConfig(TypeConfig tc, string fieldName) @@ -1355,6 +1757,22 @@ private FieldConfig GetFieldConfig(TypeConfig tc, string fieldName) return new FieldConfig(); } + private string ApplyTypeNameConvention(string asnName) + { + string pascal = ToPascalCase(asnName); + + var defaults = this.config.Defaults; + + if (!string.IsNullOrEmpty(defaults.TypePrefix) + && !defaults.ExcludePrefix.Contains(asnName) + && !pascal.StartsWith(defaults.TypePrefix, StringComparison.Ordinal)) + { + return defaults.TypePrefix + pascal; + } + + return pascal; + } + private string ResolveCSharpTypeName(string asnName) { if (this.config.Types.TryGetValue(asnName, out var tc) && tc.CSharpName != null) @@ -1362,7 +1780,7 @@ private string ResolveCSharpTypeName(string asnName) return tc.CSharpName; } - return ToPascalCase(asnName); + return this.ApplyTypeNameConvention(asnName); } private string GetChoiceFieldCSharpType(Asn1Field field, FieldConfig fc) @@ -1625,8 +2043,18 @@ public static string ToPascalCase(string name) { if (part.Length > 0) { - sb.Append(char.ToUpperInvariant(part[0])); - sb.Append(part.Substring(1)); + // If the entire part is uppercase (like "AS", "REQ", "KDC"), + // convert to title case (As, Req, Kdc) for C# conventions + if (part.Length > 1 && part == part.ToUpperInvariant()) + { + sb.Append(char.ToUpperInvariant(part[0])); + sb.Append(part.Substring(1).ToLowerInvariant()); + } + else + { + sb.Append(char.ToUpperInvariant(part[0])); + sb.Append(part.Substring(1)); + } } } diff --git a/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs b/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs index 2db792ea..dace6b6e 100644 --- a/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs +++ b/Tools/Asn1SourceGenerator/Asn1IncrementalGenerator.cs @@ -119,6 +119,21 @@ private static void MergeSimpleJsonConfig(EmitterConfig config, string json) json = Regex.Replace(json, @"//[^\n]*", ""); json = json.Trim(); + // Parse "Defaults" section if present + int defaultsStart = json.IndexOf("\"Defaults\""); + if (defaultsStart >= 0) + { + int defaultsObjStart = json.IndexOf('{', defaultsStart + 10); + if (defaultsObjStart >= 0) + { + string defaultsContent = ExtractBalancedBraces(json, defaultsObjStart); + if (defaultsContent != null) + { + ParseDefaultsConfig(defaultsContent, config.Defaults); + } + } + } + // Find "Types" object int typesStart = json.IndexOf("\"Types\""); if (typesStart < 0) return; @@ -154,18 +169,54 @@ private static void MergeSimpleJsonConfig(EmitterConfig config, string json) } } + private static void ParseDefaultsConfig(string json, DefaultsConfig defaults) + { + string typePrefix = ExtractStringValue(json, "TypePrefix"); + if (typePrefix != null) defaults.TypePrefix = typePrefix; + + string ns = ExtractStringValue(json, "Namespace"); + if (ns != null) defaults.Namespace = ns; + + // Parse ExcludePrefix array: "ExcludePrefix": ["Name1", "Name2"] + int excludeStart = json.IndexOf("\"ExcludePrefix\""); + if (excludeStart >= 0) + { + int arrStart = json.IndexOf('[', excludeStart); + if (arrStart >= 0) + { + int arrEnd = json.IndexOf(']', arrStart); + if (arrEnd >= 0) + { + string arrContent = json.Substring(arrStart + 1, arrEnd - arrStart - 1); + var matches = Regex.Matches(arrContent, "\"([^\"]+)\""); + foreach (Match m in matches) + { + defaults.ExcludePrefix.Add(m.Groups[1].Value); + } + } + } + } + } + private static TypeConfig ParseTypeConfig(string json) { var tc = new TypeConfig(); - tc.CSharpName = ExtractStringValue(json, "CSharpName"); - tc.InheritsFrom = ExtractStringValue(json, "InheritsFrom"); + // Extract top-level properties only (before "Fields" section to avoid + // picking up field-level CSharpName/EnumType values) + int fieldsStart = json.IndexOf("\"Fields\""); + string topLevel = fieldsStart >= 0 ? json.Substring(0, fieldsStart) : json; + + tc.CSharpName = ExtractStringValue(topLevel, "CSharpName"); + tc.InheritsFrom = ExtractStringValue(topLevel, "InheritsFrom"); + tc.EmitWrapper = ExtractBoolValue(topLevel, "EmitWrapper"); + tc.WrapperProperty = ExtractStringValue(topLevel, "WrapperProperty"); + tc.WrapperClassName = ExtractStringValue(topLevel, "WrapperClassName"); - string ns = ExtractStringValue(json, "Namespace"); + string ns = ExtractStringValue(topLevel, "Namespace"); if (ns != null) tc.Namespace = ns; // Parse Fields object - int fieldsStart = json.IndexOf("\"Fields\""); if (fieldsStart >= 0) { int fieldsObjStart = json.IndexOf('{', fieldsStart + 8); diff --git a/version.json b/version.json index 3795b2a7..0809cbcd 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "4.6", + "version": "5.0", "publicReleaseRefSpec": [ "^refs/heads/develop", // we release out of develop "^refs/heads/rel/v\\d+\\.\\d+" // we also release branches starting with rel/vN.N From a6abbf674613e9fd847e683fe3caa90fc9c87247 Mon Sep 17 00:00:00 2001 From: Steve Syfuhs Date: Thu, 26 Mar 2026 21:36:30 -0700 Subject: [PATCH 3/4] Added commands, SPAKE, SAFE, CAMMAC, and PAC extensions --- Bruce/CommandLine/KerberosAsn1Command.cs | 339 ++++++++++++++++++ Bruce/CommandLine/KerberosCredCommand.cs | 334 +++++++++++++++++ Bruce/CommandLine/KerberosFastCommand.cs | 259 +++++++++++++ Bruce/CommandLine/KerberosInitCommand.cs | 27 ++ Bruce/CommandLine/KerberosKeytabCommand.cs | 95 +++++ Bruce/CommandLine/KerberosListCommand.cs | 5 + Bruce/CommandLine/KerberosProxyCommand.cs | 210 +++++++++++ .../CommandLine/KerberosTokenDecodeCommand.cs | 321 +++++++++++++++++ Bruce/CommandLine/KerberosWhoAmICommand.cs | 104 ++++-- Kerberos.NET/Client/KerberosMessageService.cs | 95 +++++ Kerberos.NET/Crypto/KeyUsage.cs | 1 + Kerberos.NET/Crypto/Spake/SpakeExchange.cs | 106 ++++++ Kerberos.NET/Entities/KerberosConstants.cs | 2 + .../Entities/Krb/AuthorizationDataType.cs | 8 +- .../Entities/Krb/CammacAuthorizationData.cs | 152 ++++++++ Kerberos.NET/Entities/Krb/KrbPrincipalName.cs | 11 +- Kerberos.NET/Entities/Krb/KrbPriv.cs | 25 +- Kerberos.NET/Entities/Krb/KrbSafe.cs | 80 +++++ Kerberos.NET/Entities/Krb/PaDataType.cs | 11 + Kerberos.NET/Entities/Krb/VerifierMac.cs | 16 + .../Entities/Pac/PacAttributesInfo.cs | 48 +++ Kerberos.NET/Entities/Pac/PacDeviceInfo.cs | 12 + Kerberos.NET/Entities/Pac/PacRequestor.cs | 57 +++ Kerberos.NET/Entities/Pac/PacType.cs | 7 +- .../Pac/PrivilegedAttributeCertificate.cs | 68 +++- Kerberos.NET/Entities/PrincipalNameType.cs | 5 + Kerberos.NET/Kerberos.NET.csproj | 5 + Kerberos.NET/Server/KdcAsReqMessageHandler.cs | 15 + .../Server/KdcTgsReqMessageHandler.cs | 67 ++++ Kerberos.NET/Server/PaDataFreshnessHandler.cs | 91 +++++ Kerberos.NET/Server/PaDataSpakeHandler.cs | 318 ++++++++++++++++ Kerberos.NET/Server/SpakeState.cs | 34 ++ Kerberos.NET/kerberos.asn | 16 + Kerberos.NET/kerberos.asn.json | 20 ++ .../CommandLine/KerberosAsn1CommandTests.cs | 267 ++++++++++++++ .../CommandLine/KerberosCredCommandTests.cs | 239 ++++++++++++ .../CommandLine/KerberosFastCommandTests.cs | 153 ++++++++ .../KerberosKeytabMergeRemoveTests.cs | 283 +++++++++++++++ .../CommandLine/KerberosProxyCommandTests.cs | 130 +++++++ .../KerberosTokenDecodeCommandTests.cs | 176 +++++++++ .../KerberosWhoAmIExtendedTests.cs | 91 +++++ .../CommandLine/TransportSelectionTests.cs | 188 ++++++++++ .../Entities/AnonymousPkinitTests.cs | 83 +++++ .../Entities/CammacTests.cs | 182 ++++++++++ .../Entities/KrbSafePrivTests.cs | 221 ++++++++++++ .../Tests.Kerberos.NET/Kdc/FreshnessTests.cs | 176 +++++++++ Tests/Tests.Kerberos.NET/Kdc/RbcdTests.cs | 64 ++++ Tests/Tests.Kerberos.NET/Kdc/SpakeTests.cs | 222 ++++++++++++ .../Pac/PacExtensionTests.cs | 136 +++++++ 49 files changed, 5549 insertions(+), 26 deletions(-) create mode 100644 Bruce/CommandLine/KerberosAsn1Command.cs create mode 100644 Bruce/CommandLine/KerberosCredCommand.cs create mode 100644 Bruce/CommandLine/KerberosFastCommand.cs create mode 100644 Bruce/CommandLine/KerberosProxyCommand.cs create mode 100644 Bruce/CommandLine/KerberosTokenDecodeCommand.cs create mode 100644 Kerberos.NET/Client/KerberosMessageService.cs create mode 100644 Kerberos.NET/Crypto/Spake/SpakeExchange.cs create mode 100644 Kerberos.NET/Entities/Krb/CammacAuthorizationData.cs create mode 100644 Kerberos.NET/Entities/Krb/KrbSafe.cs create mode 100644 Kerberos.NET/Entities/Krb/VerifierMac.cs create mode 100644 Kerberos.NET/Entities/Pac/PacAttributesInfo.cs create mode 100644 Kerberos.NET/Entities/Pac/PacDeviceInfo.cs create mode 100644 Kerberos.NET/Entities/Pac/PacRequestor.cs create mode 100644 Kerberos.NET/Server/PaDataFreshnessHandler.cs create mode 100644 Kerberos.NET/Server/PaDataSpakeHandler.cs create mode 100644 Kerberos.NET/Server/SpakeState.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosAsn1CommandTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosCredCommandTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosFastCommandTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosKeytabMergeRemoveTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosProxyCommandTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosTokenDecodeCommandTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/KerberosWhoAmIExtendedTests.cs create mode 100644 Tests/Tests.Kerberos.NET/CommandLine/TransportSelectionTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Entities/AnonymousPkinitTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Entities/CammacTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Entities/KrbSafePrivTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Kdc/FreshnessTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Kdc/RbcdTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Kdc/SpakeTests.cs create mode 100644 Tests/Tests.Kerberos.NET/Pac/PacExtensionTests.cs diff --git a/Bruce/CommandLine/KerberosAsn1Command.cs b/Bruce/CommandLine/KerberosAsn1Command.cs new file mode 100644 index 00000000..f18f65ef --- /dev/null +++ b/Bruce/CommandLine/KerberosAsn1Command.cs @@ -0,0 +1,339 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Formats.Asn1; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Kerberos.NET.CommandLine +{ + [CommandLineCommand("kasn1|asn1|asn", Description = "KerberosAsn1")] + public class KerberosAsn1Command : BaseCommand + { + public KerberosAsn1Command(CommandLineParameters parameters) + : base(parameters) + { + } + + [CommandLineParameter("data", FormalParameter = true, Description = "Data")] + public string Data { get; set; } + + [CommandLineParameter("f|file", Description = "File")] + public string InputFile { get; set; } + + [CommandLineParameter("hex", Description = "Hex")] + public bool IsHex { get; set; } + + [CommandLineParameter("base64", Description = "Base64")] + public bool IsBase64 { get; set; } + + [CommandLineParameter("depth", Description = "Depth")] + public string MaxDepthStr { get; set; } + + [CommandLineParameter("v|verbose", Description = "Verbose")] + public override bool Verbose { get; set; } + + public override async Task Execute() + { + if (await base.Execute()) + { + return true; + } + + byte[] bytes = null; + + if (!string.IsNullOrWhiteSpace(this.InputFile)) + { + var path = Environment.ExpandEnvironmentVariables(this.InputFile); + + if (!File.Exists(path)) + { + this.WriteLineError("File not found: {File}", path); + return false; + } + + bytes = await File.ReadAllBytesAsync(path); + } + else if (!string.IsNullOrWhiteSpace(this.Data)) + { + bytes = this.ParseInput(this.Data); + } + + if (bytes == null || bytes.Length == 0) + { + this.WriteLineError("No input data provided. Pass hex, base64, or use --file"); + return false; + } + + int maxDepth = 50; + + if (!string.IsNullOrWhiteSpace(this.MaxDepthStr) && int.TryParse(this.MaxDepthStr, out int parsed)) + { + maxDepth = parsed; + } + + this.WriteLine(); + this.WriteHeader($"ASN.1 Structure ({bytes.Length} bytes)"); + this.WriteLine(); + + try + { + var reader = new AsnReader(new ReadOnlyMemory(bytes), AsnEncodingRules.DER); + this.DumpAsn1(reader, 0, maxDepth); + } + catch (Exception ex) + { + this.WriteLineError("Failed to parse ASN.1: {Error}", ex.Message); + } + + return true; + } + + private byte[] ParseInput(string input) + { + input = input.Trim(); + + if (this.IsHex) + { + return ParseHex(input); + } + + if (this.IsBase64) + { + return Convert.FromBase64String(input); + } + + // Auto-detect + try + { + return Convert.FromBase64String(input); + } + catch + { + // not base64 + } + + try + { + return ParseHex(input); + } + catch + { + // not hex + } + + this.WriteLineError("Cannot parse input as base64 or hex"); + return null; + } + + private static byte[] ParseHex(string hex) + { + hex = hex.Replace(" ", "").Replace("-", "").Replace(":", "").Replace("\n", "").Replace("\r", ""); + + var bytes = new byte[hex.Length / 2]; + + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + } + + return bytes; + } + + private void DumpAsn1(AsnReader reader, int depth, int maxDepth) + { + if (depth > maxDepth) + { + this.WriteLine(depth, "... (max depth reached)"); + return; + } + + while (reader.HasData) + { + var tag = reader.PeekTag(); + var indent = new string(' ', depth * 2); + string tagName = GetTagName(tag); + + if (tag.IsConstructed) + { + this.WriteLineRaw($"{indent}{tagName} {{"); + + try + { + AsnReader inner; + + if (tag.TagClass == TagClass.ContextSpecific) + { + inner = reader.ReadSequence(tag); + } + else if (tag.TagClass == TagClass.Application) + { + inner = reader.ReadSequence(tag); + } + else + { + inner = reader.ReadSequence(tag); + } + + this.DumpAsn1(inner, depth + 1, maxDepth); + } + catch + { + // fallback: read raw value + var raw = reader.ReadEncodedValue(); + this.WriteLineRaw($"{indent} ({raw.Length} bytes)"); + } + + this.WriteLineRaw($"{indent}}}"); + } + else + { + this.DumpPrimitive(reader, tag, indent, tagName); + } + } + } + + private void DumpPrimitive(AsnReader reader, Asn1Tag tag, string indent, string tagName) + { + try + { + if (tag.TagClass == TagClass.Universal) + { + switch ((UniversalTagNumber)tag.TagValue) + { + case UniversalTagNumber.Integer: + if (reader.TryReadInt32(out int intVal)) + { + this.WriteLineRaw($"{indent}{tagName}: {intVal}"); + } + else + { + var bigInt = reader.ReadIntegerBytes(); + this.WriteLineRaw($"{indent}{tagName}: 0x{ToHexString(bigInt.Span)}"); + } + return; + + case UniversalTagNumber.Boolean: + this.WriteLineRaw($"{indent}{tagName}: {reader.ReadBoolean()}"); + return; + + case UniversalTagNumber.ObjectIdentifier: + var oid = reader.ReadObjectIdentifier(); + this.WriteLineRaw($"{indent}{tagName}: {oid}"); + return; + + case UniversalTagNumber.OctetString: + var octetData = reader.ReadOctetString(); + if (octetData.Length <= 32) + { + this.WriteLineRaw($"{indent}{tagName}: [{octetData.Length}] 0x{ToHexString(octetData)}"); + } + else + { + this.WriteLineRaw($"{indent}{tagName}: [{octetData.Length} bytes]"); + } + return; + + case UniversalTagNumber.BitString: + var bitData = reader.ReadBitString(out int unusedBits); + this.WriteLineRaw($"{indent}{tagName}: [{bitData.Length} bytes, {unusedBits} unused bits]"); + return; + + case UniversalTagNumber.GeneralizedTime: + this.WriteLineRaw($"{indent}{tagName}: {reader.ReadGeneralizedTime()}"); + return; + + case UniversalTagNumber.Null: + reader.ReadNull(); + this.WriteLineRaw($"{indent}{tagName}"); + return; + + case UniversalTagNumber.UTF8String: + case UniversalTagNumber.IA5String: + case UniversalTagNumber.PrintableString: + case UniversalTagNumber.VisibleString: + case UniversalTagNumber.GeneralString: + var str = reader.ReadCharacterString((UniversalTagNumber)tag.TagValue); + this.WriteLineRaw($"{indent}{tagName}: \"{str}\""); + return; + + case UniversalTagNumber.Enumerated: + var enumBytes = reader.ReadEnumeratedBytes(); + this.WriteLineRaw($"{indent}{tagName}: {ToHexString(enumBytes.Span)}"); + return; + } + } + + // Fallback for context-specific or unhandled primitives + var raw = reader.ReadEncodedValue(); + this.WriteLineRaw($"{indent}{tagName}: [{raw.Length} bytes]"); + } + catch + { + var raw = reader.ReadEncodedValue(); + this.WriteLineRaw($"{indent}{tagName}: [{raw.Length} bytes]"); + } + } + + private static string GetTagName(Asn1Tag tag) + { + if (tag.TagClass == TagClass.ContextSpecific) + { + return $"[{tag.TagValue}]"; + } + + if (tag.TagClass == TagClass.Application) + { + string appName = tag.TagValue switch + { + 1 => "Ticket", + 2 => "Authenticator", + 3 => "EncTicketPart", + 10 => "AS-REQ", + 11 => "AS-REP", + 12 => "TGS-REQ", + 13 => "TGS-REP", + 14 => "AP-REQ", + 15 => "AP-REP", + 20 => "KRB-SAFE", + 21 => "KRB-PRIV", + 22 => "KRB-CRED", + 25 => "EncASRepPart", + 26 => "EncTGSRepPart", + 27 => "EncAPRepPart", + 28 => "EncKrbPrivPart", + 29 => "EncKrbCredPart", + 30 => "KRB-ERROR", + _ => null + }; + + return appName != null + ? $"APPLICATION {tag.TagValue} ({appName})" + : $"APPLICATION {tag.TagValue}"; + } + + if (tag.TagClass == TagClass.Universal) + { + return ((UniversalTagNumber)tag.TagValue).ToString(); + } + + return $"PRIVATE {tag.TagValue}"; + } + + private static string ToHexString(ReadOnlySpan bytes) + { + var sb = new StringBuilder(bytes.Length * 2); + + for (int i = 0; i < bytes.Length; i++) + { + sb.Append(bytes[i].ToString("x2")); + } + + return sb.ToString(); + } + } +} diff --git a/Bruce/CommandLine/KerberosCredCommand.cs b/Bruce/CommandLine/KerberosCredCommand.cs new file mode 100644 index 00000000..0f5ea7d6 --- /dev/null +++ b/Bruce/CommandLine/KerberosCredCommand.cs @@ -0,0 +1,334 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Kerberos.NET.Client; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.CommandLine +{ + [CommandLineCommand("kcred|cred", Description = "KerberosCred")] + public class KerberosCredCommand : BaseCommand + { + public KerberosCredCommand(CommandLineParameters parameters) + : base(parameters) + { + } + + [CommandLineParameter("v|verbose", Description = "Verbose")] + public override bool Verbose { get; set; } + + [CommandLineParameter("c|cache", Description = "Cache")] + public string Cache { get; set; } + + [CommandLineParameter("i|import", Description = "Import")] + public string ImportData { get; set; } + + [CommandLineParameter("f|file", Description = "File")] + public string FilePath { get; set; } + + [CommandLineParameter("e|export", Description = "Export")] + public string ExportSpn { get; set; } + + [CommandLineParameter("d|decode", Description = "Decode")] + public string DecodeData { get; set; } + + public override async Task Execute() + { + if (await base.Execute()) + { + return true; + } + + this.WriteLine(); + + if (!string.IsNullOrWhiteSpace(this.DecodeData) || !string.IsNullOrWhiteSpace(this.FilePath) && string.IsNullOrWhiteSpace(this.ImportData)) + { + this.DecodeKrbCred(); + return true; + } + + var client = this.CreateClient(verbose: this.Verbose); + + if (!string.IsNullOrWhiteSpace(this.Cache)) + { + client.Configuration.Defaults.DefaultCCacheName = this.Cache; + } + + if (!string.IsNullOrWhiteSpace(this.ImportData)) + { + await this.ImportTicket(client); + } + + if (!string.IsNullOrWhiteSpace(this.ExportSpn)) + { + this.ExportTicket(client); + } + + if (string.IsNullOrWhiteSpace(this.ImportData) && + string.IsNullOrWhiteSpace(this.ExportSpn) && + string.IsNullOrWhiteSpace(this.DecodeData)) + { + this.WriteLineError("Specify --import, --export, --decode, or --file to decode a KRB-CRED"); + return false; + } + + return true; + } + + private void DecodeKrbCred() + { + byte[] bytes = null; + + if (!string.IsNullOrWhiteSpace(this.FilePath)) + { + var path = Environment.ExpandEnvironmentVariables(this.FilePath); + + if (!File.Exists(path)) + { + this.WriteLineError("File not found: {File}", path); + return; + } + + bytes = File.ReadAllBytes(path); + + // Check if file content is base64 + try + { + var text = System.Text.Encoding.UTF8.GetString(bytes).Trim(); + bytes = Convert.FromBase64String(text); + } + catch + { + // Not base64, use raw bytes + } + } + else if (!string.IsNullOrWhiteSpace(this.DecodeData)) + { + try + { + bytes = Convert.FromBase64String(this.DecodeData.Trim()); + } + catch (FormatException) + { + this.WriteLineError("Input is not valid base64"); + return; + } + } + + if (bytes == null || bytes.Length == 0) + { + this.WriteLineError("No data to decode"); + return; + } + + KrbCred cred; + + try + { + cred = KrbCred.DecodeApplication(bytes); + } + catch (Exception ex) + { + this.WriteLineError("Failed to decode KRB-CRED: {Error}", ex.Message); + return; + } + + this.DisplayKrbCred(cred); + } + + private void DisplayKrbCred(KrbCred cred) + { + var props = new List<(string, object)> + { + (null, "KRB-CRED"), + ("Protocol Version", cred.ProtocolVersionNumber), + ("Message Type", cred.MessageType), + ("Tickets", cred.Tickets?.Length ?? 0), + ("Encrypted Part EType", cred.EncryptedPart?.EType), + }; + + this.WriteProperties(props); + this.WriteLine(); + + if (cred.Tickets != null) + { + for (var i = 0; i < cred.Tickets.Length; i++) + { + var ticket = cred.Tickets[i]; + + var ticketProps = new List<(string, object)> + { + (null, $"Ticket #{i}"), + ("Realm", ticket.Realm), + ("Server Name", ticket.SName?.FullyQualifiedName), + ("Encryption Type", ticket.EncryptedPart?.EType), + ("Key Version", ticket.EncryptedPart?.KeyVersionNumber), + }; + + this.WriteProperties(ticketProps); + this.WriteLine(); + } + } + + if (cred.EncryptedPart?.EType == EncryptionType.NULL) + { + try + { + var credPart = cred.Validate(); + this.DisplayCredPart(credPart); + } + catch (Exception ex) + { + if (this.Verbose) + { + this.WriteLineWarning("Could not decode EncKrbCredPart: {Error}", ex.Message); + } + } + } + } + + private void DisplayCredPart(KrbEncKrbCredPart credPart) + { + if (credPart.TicketInfo == null) + { + return; + } + + for (var i = 0; i < credPart.TicketInfo.Length; i++) + { + var info = credPart.TicketInfo[i]; + + var infoProps = new List<(string, object)> + { + (null, $"Credential Info #{i}"), + ("Principal", info.PName?.FullyQualifiedName), + ("Realm", info.Realm), + ("Server", info.SName?.FullyQualifiedName), + ("Server Realm", info.SRealm), + ("Key Type", info.Key?.EType), + ("Flags", info.Flags), + ("Auth Time", info.AuthTime), + ("Start Time", info.StartTime), + ("End Time", info.EndTime), + ("Renew Till", info.RenewTill), + }; + + this.WriteProperties(infoProps); + this.WriteLine(); + } + } + + private async Task ImportTicket(KerberosClient client) + { + byte[] bytes; + + if (!string.IsNullOrWhiteSpace(this.FilePath)) + { + var path = Environment.ExpandEnvironmentVariables(this.FilePath); + + if (!File.Exists(path)) + { + this.WriteLineError("File not found: {File}", path); + return; + } + + bytes = File.ReadAllBytes(path); + } + else + { + try + { + bytes = Convert.FromBase64String(this.ImportData.Trim()); + } + catch (FormatException) + { + this.WriteLineError("Import data is not valid base64"); + return; + } + } + + KrbCred cred; + + try + { + cred = KrbCred.DecodeApplication(bytes); + } + catch (Exception ex) + { + this.WriteLineError("Failed to decode KRB-CRED: {Error}", ex.Message); + return; + } + + for (var i = 0; i < cred.Tickets.Length; i++) + { + var ticket = cred.Tickets[i]; + var spn = ticket.SName?.FullyQualifiedName ?? "unknown"; + + this.WriteLine("Importing ticket for {Spn}", spn); + } + + client.ImportCredential(cred); + + this.WriteLine(); + this.WriteLine("Imported {Count} ticket(s)", cred.Tickets.Length); + } + + private void ExportTicket(KerberosClient client) + { + var entry = client.Cache.GetCacheItem(this.ExportSpn); + + if (entry == null) + { + this.WriteLineError("No cached ticket found for {Spn}", this.ExportSpn); + return; + } + + var ticket = entry.KdcResponse?.Ticket; + + if (ticket == null) + { + this.WriteLineError("Cache entry has no ticket for {Spn}", this.ExportSpn); + return; + } + + var credInfo = new KrbCredInfo + { + Key = entry.SessionKey, + Realm = entry.KdcResponse.CRealm, + PName = entry.KdcResponse.CName, + Flags = entry.Flags, + AuthTime = entry.AuthTime, + StartTime = entry.StartTime, + EndTime = entry.EndTime, + RenewTill = entry.RenewTill, + SName = ticket.SName, + SRealm = ticket.Realm, + }; + + var krbCred = KrbCred.WrapTicket(ticket, credInfo); + var encoded = krbCred.EncodeApplication(); + + if (!string.IsNullOrWhiteSpace(this.FilePath)) + { + var path = Environment.ExpandEnvironmentVariables(this.FilePath); + File.WriteAllBytes(path, encoded.ToArray()); + this.WriteLine("Exported ticket to {File}", path); + } + else + { + this.WriteHeader("KRB-CRED (base64)"); + this.WriteLine(1, "{Data}", Convert.ToBase64String(encoded.ToArray())); + } + + this.WriteLine(); + } + } +} diff --git a/Bruce/CommandLine/KerberosFastCommand.cs b/Bruce/CommandLine/KerberosFastCommand.cs new file mode 100644 index 00000000..c5193486 --- /dev/null +++ b/Bruce/CommandLine/KerberosFastCommand.cs @@ -0,0 +1,259 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Kerberos.NET.Client; +using Kerberos.NET.Credentials; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.CommandLine +{ + [CommandLineCommand("kfast|fast", Description = "KerberosFast")] + public class KerberosFastCommand : BaseCommand + { + public KerberosFastCommand(CommandLineParameters parameters) + : base(parameters) + { + } + + [CommandLineParameter("v|verbose", Description = "Verbose")] + public override bool Verbose { get; set; } + + [CommandLineParameter("c|cache", Description = "Cache")] + public string Cache { get; set; } + + [CommandLineParameter("realm", FormalParameter = true, Description = "Realm")] + public override string Realm { get; set; } + + [CommandLineParameter("probe", Description = "Probe")] + public bool Probe { get; set; } + + [CommandLineParameter("test-cf2", Description = "TestCf2")] + public bool TestCf2 { get; set; } + + public override async Task Execute() + { + if (await base.Execute()) + { + return true; + } + + if (string.IsNullOrWhiteSpace(this.Realm)) + { + this.Realm = this.DefaultRealm; + } + + this.WriteLine(); + + if (this.TestCf2) + { + this.RunCf2Test(); + return true; + } + + var client = this.CreateClient(verbose: this.Verbose); + + if (!string.IsNullOrWhiteSpace(this.Cache)) + { + client.Configuration.Defaults.DefaultCCacheName = this.Cache; + } + + if (this.Probe || !this.TestCf2) + { + await this.ProbeFastSupport(client); + } + + return true; + } + + private async Task ProbeFastSupport(KerberosClient client) + { + this.WriteHeader("FAST Support Probe"); + this.WriteLine(); + + if (string.IsNullOrWhiteSpace(this.Realm)) + { + this.WriteLineError("A realm is required for FAST probing"); + return; + } + + this.WriteLine(" Realm: {Realm}", this.Realm); + this.WriteLine(); + + // Attempt authentication with a dummy principal to trigger PREAUTH_REQUIRED, + // which reveals what pre-auth methods the KDC advertises. + try + { + var cred = new KerberosPasswordCredential("fast-probe", "probe", this.Realm); + await client.Authenticate(cred); + + // Unexpected success + this.WriteHeader("KDC responded with AS-REP (unexpected for probe)"); + } + catch (KerberosProtocolException kex) when (kex.Error != null) + { + this.DisplayProbeResult(kex.Error); + } + catch (Exception ex) + { + this.WriteLineError("Probe failed: {Error}", ex.Message); + + if (this.Verbose) + { + this.WriteLine(1, "{StackTrace}", ex.StackTrace); + } + } + } + + private void DisplayProbeResult(KrbError error) + { + var props = new List<(string, object)> + { + (null, "KDC Response"), + ("Error Code", error.ErrorCode), + ("Error Text", error.EText), + ("Server Time", error.STime), + }; + + this.WriteProperties(props); + this.WriteLine(); + + if (error.ErrorCode == KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED && error.EData.HasValue) + { + try + { + var methodData = KrbMethodData.Decode(error.EData.Value); + + this.WriteHeader("Advertised Pre-Authentication Methods"); + this.WriteLine(); + + bool fastAdvertised = false; + bool encChallengeAdvertised = false; + bool encTimestampAdvertised = false; + bool etypeInfo2Advertised = false; + bool reqEncPaRep = false; + + foreach (var method in methodData.MethodData) + { + this.WriteLine(1, " PA-DATA Type: {Type} ({TypeInt})", method.Type, (int)method.Type); + + switch (method.Type) + { + case PaDataType.PA_FX_FAST: + fastAdvertised = true; + break; + case PaDataType.PA_ENCRYPTED_CHALLENGE: + encChallengeAdvertised = true; + break; + case PaDataType.PA_ENC_TIMESTAMP: + encTimestampAdvertised = true; + break; + case PaDataType.PA_ETYPE_INFO2: + etypeInfo2Advertised = true; + + if (this.Verbose && method.Value.Length > 0) + { + try + { + var etypeInfo2 = KrbETypeInfo2.Decode(method.Value); + + foreach (var entry in etypeInfo2.ETypeInfo) + { + this.WriteLine(2, " EType: {EType}, Salt: {Salt}", + entry.EType, entry.Salt ?? "(none)"); + } + } + catch { } + } + break; + case PaDataType.PA_REQ_ENC_PA_REP: + reqEncPaRep = true; + break; + } + } + + this.WriteLine(); + this.WriteHeader("FAST Assessment"); + this.WriteLine(); + + this.WriteLine(1, " PA-FX-FAST Advertised: {Value}", fastAdvertised ? "Yes" : "No"); + this.WriteLine(1, " Encrypted Challenge: {Value}", encChallengeAdvertised ? "Yes" : "No"); + this.WriteLine(1, " Encrypted Timestamp: {Value}", encTimestampAdvertised ? "Yes" : "No"); + this.WriteLine(1, " EType-Info2: {Value}", etypeInfo2Advertised ? "Yes" : "No"); + this.WriteLine(1, " Enc PA-REP Request: {Value}", reqEncPaRep ? "Yes" : "No"); + this.WriteLine(); + + if (fastAdvertised) + { + this.WriteLine(" KDC supports FAST (RFC 6113)"); + } + else + { + this.WriteLineWarning(" KDC does not advertise FAST support"); + } + } + catch (Exception ex) + { + this.WriteLineError("Failed to decode PA-DATA: {Error}", ex.Message); + } + } + else + { + this.WriteLineWarning("KDC did not return PREAUTH_REQUIRED (cannot determine FAST support)"); + } + + this.WriteLine(); + } + + private void RunCf2Test() + { + this.WriteHeader("KRB-FX-CF2 Test (RFC 6113 Test Vectors)"); + this.WriteLine(); + + // RFC 6113 Section A test vectors + var key1 = new byte[16]; + var key2 = new byte[16]; + + for (int i = 0; i < 16; i++) + { + key1[i] = (byte)(i + 1); + key2[i] = (byte)(i + 0x11); + } + + var pepper1 = Encoding.UTF8.GetBytes("a]"); + var pepper2 = Encoding.UTF8.GetBytes("b]"); + + try + { + var result = KrbFx.Cf2(key1, key2, pepper1, pepper2, EncryptionType.AES128_CTS_HMAC_SHA1_96); + + this.WriteLine(" Key1: {Key}", BitConverter.ToString(key1).Replace("-", "").ToLower()); + this.WriteLine(" Key2: {Key}", BitConverter.ToString(key2).Replace("-", "").ToLower()); + this.WriteLine(" Pepper1: {Pepper}", "a]"); + this.WriteLine(" Pepper2: {Pepper}", "b]"); + this.WriteLine(" EType: {EType}", EncryptionType.AES128_CTS_HMAC_SHA1_96); + this.WriteLine(); + this.WriteLine(" CF2 Result: {Result}", BitConverter.ToString(result.ToArray()).Replace("-", "").ToLower()); + this.WriteLine(); + this.WriteLine(" CF2 derivation completed successfully"); + } + catch (Exception ex) + { + this.WriteLineError("CF2 test failed: {Error}", ex.Message); + + if (this.Verbose) + { + this.WriteLine(1, "{StackTrace}", ex.StackTrace); + } + } + + this.WriteLine(); + } + } +} diff --git a/Bruce/CommandLine/KerberosInitCommand.cs b/Bruce/CommandLine/KerberosInitCommand.cs index e9e5f1d8..05d6e02c 100644 --- a/Bruce/CommandLine/KerberosInitCommand.cs +++ b/Bruce/CommandLine/KerberosInitCommand.cs @@ -14,6 +14,7 @@ using Kerberos.NET.Configuration; using Kerberos.NET.Credentials; using Kerberos.NET.Crypto; +using Kerberos.NET.Transport; namespace Kerberos.NET.CommandLine { @@ -121,6 +122,9 @@ public KerberosInitCommand(CommandLineParameters parameters) [CommandLineParameter("password", Description = "Password")] public string Password { get; set; } + [CommandLineParameter("transport", Description = "Transport")] + public string Transport { get; set; } + public override async Task Execute() { if (await base.Execute()) @@ -147,6 +151,8 @@ public override async Task Execute() client.Configuration.Defaults.DefaultCCacheName = this.Cache; } + ConfigureTransport(client, this.Transport); + this.SetClientProperties(client); var cred = this.ParseCredential(client.Configuration); @@ -214,6 +220,27 @@ public override async Task Execute() return true; } + public static void ConfigureTransport(KerberosClient client, string transport) + { + if (string.IsNullOrWhiteSpace(transport)) + { + return; + } + + var selected = transport.ToLowerInvariant(); + + foreach (var t in client.Transports) + { + t.Enabled = selected switch + { + "tcp" => t is TcpKerberosTransport, + "udp" => t is UdpKerberosTransport, + "https" or "proxy" => t is HttpsKerberosTransport, + _ => t.Enabled, + }; + } + } + private void SetClientProperties(KerberosClient client) { SetClientProperty(this.Proxy, client, AuthenticationOptions.Proxy); diff --git a/Bruce/CommandLine/KerberosKeytabCommand.cs b/Bruce/CommandLine/KerberosKeytabCommand.cs index f2378f2c..4529141d 100644 --- a/Bruce/CommandLine/KerberosKeytabCommand.cs +++ b/Bruce/CommandLine/KerberosKeytabCommand.cs @@ -72,6 +72,18 @@ public KerberosKeytabCommand(CommandLineParameters parameters) [CommandLineParameter("verify", Description = "Verify")] public bool Verify { get; set; } + [CommandLineParameter("merge", Description = "Merge")] + public string MergeFrom { get; set; } + + [CommandLineParameter("remove", Description = "Remove")] + public string RemovePrincipal { get; set; } + + [CommandLineParameter("remove-etype", Description = "RemoveEType")] + public EncryptionType? RemoveEncryptionType { get; set; } + + [CommandLineParameter("o|output", Description = "Output")] + public string OutputFile { get; set; } + public override async Task Execute() { if (await base.Execute()) @@ -89,6 +101,10 @@ public override async Task Execute() { await this.AppendKeyToFile(); } + else if (!string.IsNullOrWhiteSpace(this.MergeFrom) || !string.IsNullOrWhiteSpace(this.RemovePrincipal)) + { + await this.MergeOrRemoveEntries(); + } else { await this.DisplayKey(); @@ -433,6 +449,85 @@ private async Task> PingForSalt(KerberosClient c return null; } + private async Task MergeOrRemoveEntries() + { + string file = this.GetKeytabFile(); + + if (!File.Exists(file)) + { + this.WriteLineError(SR.Resource("CommandLine_KerberosKeytab_UnknownFile", file)); + return; + } + + var bytes = await File.ReadAllBytesAsync(file); + var keytab = new KeyTable(bytes); + + if (!string.IsNullOrWhiteSpace(this.MergeFrom)) + { + var mergePath = Environment.ExpandEnvironmentVariables(this.MergeFrom); + + if (!File.Exists(mergePath)) + { + this.WriteLineError("Merge source keytab not found: {File}", mergePath); + return; + } + + var source = new KeyTable(File.ReadAllBytes(mergePath)); + var added = 0; + + foreach (var entry in source.Entries) + { + if (!keytab.Entries.Any(e => e.Equals(entry))) + { + keytab.Entries.Add(entry); + added++; + } + } + + this.WriteLine("Merged {Count} entries from {File}", added, mergePath); + this.WriteLine(); + } + + if (!string.IsNullOrWhiteSpace(this.RemovePrincipal)) + { + var toRemove = keytab.Entries.Where(e => + { + var principalMatch = e.Principal?.FullyQualifiedName?.IndexOf( + this.RemovePrincipal, StringComparison.OrdinalIgnoreCase) >= 0; + + if (principalMatch && this.RemoveEncryptionType.HasValue) + { + return e.EncryptionType == this.RemoveEncryptionType.Value; + } + + return principalMatch; + }).ToList(); + + foreach (var entry in toRemove) + { + keytab.Entries.Remove(entry); + } + + this.WriteLine("Removed {Count} entries matching '{Principal}'", toRemove.Count, this.RemovePrincipal); + this.WriteLine(); + } + + var outputPath = this.OutputFile ?? file; + outputPath = Environment.ExpandEnvironmentVariables(outputPath); + + using (var fs = new FileStream(outputPath, FileMode.Create)) + using (var writer = new BinaryWriter(fs)) + { + keytab.Write(writer); + writer.Flush(); + } + + this.WriteLine("Keytab written to {File} ({Count} entries)", outputPath, keytab.Entries.Count); + this.WriteLine(); + + await this.DumpKeytab(outputPath); + } + private async Task DumpKeytab(string file) { var bytes = await File.ReadAllBytesAsync(file); diff --git a/Bruce/CommandLine/KerberosListCommand.cs b/Bruce/CommandLine/KerberosListCommand.cs index 63caea83..bf166e71 100644 --- a/Bruce/CommandLine/KerberosListCommand.cs +++ b/Bruce/CommandLine/KerberosListCommand.cs @@ -57,6 +57,9 @@ public KerberosListCommand(CommandLineParameters parameters) [CommandLineParameter("renew", Description = "RenewTicket")] public bool RenewTgt { get; set; } + [CommandLineParameter("transport", Description = "Transport")] + public string Transport { get; set; } + public override async Task Execute() { if (await base.Execute()) @@ -68,6 +71,8 @@ public override async Task Execute() var client = this.CreateClient(verbose: this.Verbose); + KerberosInitCommand.ConfigureTransport(client, this.Transport); + if (!string.IsNullOrWhiteSpace(this.Cache)) { if (!this.Cache.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase)) diff --git a/Bruce/CommandLine/KerberosProxyCommand.cs b/Bruce/CommandLine/KerberosProxyCommand.cs new file mode 100644 index 00000000..9e8d4a06 --- /dev/null +++ b/Bruce/CommandLine/KerberosProxyCommand.cs @@ -0,0 +1,210 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Kerberos.NET.Client; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Transport; + +namespace Kerberos.NET.CommandLine +{ + [CommandLineCommand("kproxy|proxy", Description = "KerberosProxy")] + public class KerberosProxyCommand : BaseCommand + { + public KerberosProxyCommand(CommandLineParameters parameters) + : base(parameters) + { + } + + [CommandLineParameter("realm", FormalParameter = true, Description = "Realm")] + public override string Realm { get; set; } + + [CommandLineParameter("v|verbose", Description = "Verbose")] + public override bool Verbose { get; set; } + + [CommandLineParameter("url", Description = "Url")] + public string ProxyUrl { get; set; } + + [CommandLineParameter("timeout", Description = "Timeout")] + public TimeSpan? Timeout { get; set; } + + public override async Task Execute() + { + if (await base.Execute()) + { + return true; + } + + if (string.IsNullOrWhiteSpace(this.Realm)) + { + this.Realm = this.DefaultRealm; + } + + if (string.IsNullOrWhiteSpace(this.Realm)) + { + this.WriteLineError("A realm is required. Specify as a parameter or set USERDNSDOMAIN."); + return false; + } + + this.WriteLine(); + this.WriteHeader("KDC Proxy Connectivity Test"); + this.WriteLine(); + + await this.TestProxyConnectivity(); + + return true; + } + + private async Task TestProxyConnectivity() + { + var timeout = this.Timeout ?? TimeSpan.FromSeconds(10); + + using var cts = new CancellationTokenSource(timeout); + + var transport = new HttpsKerberosTransport(); + transport.ConnectTimeout = timeout; + + if (!string.IsNullOrWhiteSpace(this.ProxyUrl)) + { + transport.DomainPaths[this.Realm.ToLowerInvariant()] = new Uri(this.ProxyUrl); + } + + this.WriteLine(" Realm: {Realm}", this.Realm); + + if (!string.IsNullOrWhiteSpace(this.ProxyUrl)) + { + this.WriteLine(" Proxy URL: {Url}", this.ProxyUrl); + } + else + { + this.WriteLine(" Discovery: DNS SRV (_kerberos._https)"); + } + + this.WriteLine(" Timeout: {Timeout}", timeout); + this.WriteLine(); + + // Build a minimal AS-REQ to probe the KDC proxy + var asReq = new KrbAsReq + { + Body = new KrbKdcReqBody + { + CName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { "probe" } + }, + Realm = this.Realm.ToUpperInvariant(), + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", this.Realm.ToUpperInvariant() } + }, + Till = DateTimeOffset.UtcNow.AddHours(1), + Nonce = (int)(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() & 0x7FFFFFFF), + EType = new[] { EncryptionType.AES256_CTS_HMAC_SHA1_96 }, + } + }; + + var encoded = asReq.EncodeApplication(); + + var sw = Stopwatch.StartNew(); + + try + { + var response = await transport.SendMessage( + this.Realm, + encoded, + cts.Token + ); + + sw.Stop(); + + this.WriteHeader("Result: Connected"); + this.WriteLine(); + this.WriteLine(" Response Size: {Size} bytes", response.Length); + this.WriteLine(" Round Trip: {Time}ms", sw.ElapsedMilliseconds); + + if (!string.IsNullOrWhiteSpace(transport.RequestId)) + { + this.WriteLine(" Request ID: {RequestId}", transport.RequestId); + } + + // Try to decode the response as a KRB-ERROR (expected for a probe) + try + { + var error = KrbError.DecodeApplication(response); + + var errorProps = new List<(string, object)> + { + ("", ""), + (null, "KDC Response"), + ("Error Code", error.ErrorCode), + ("Error Text", error.EText), + ("Server Time", error.STime), + ("Realm", error.Realm), + }; + + this.WriteProperties(errorProps); + } + catch + { + if (this.Verbose) + { + this.WriteLine(" (Response is not a KRB-ERROR)"); + } + } + } + catch (KerberosTransportException tex) + { + sw.Stop(); + + this.WriteLineError("Transport Error: {Error}", tex.Message); + this.WriteLine(); + this.WriteLine(" Time Elapsed: {Time}ms", sw.ElapsedMilliseconds); + + if (tex.Error != null) + { + this.WriteLine(" KDC Error: {Error}", tex.Error.ErrorCode); + } + } + catch (HttpRequestException hex) + { + sw.Stop(); + + this.WriteLineError("HTTP Error: {Error}", hex.Message); + this.WriteLine(); + this.WriteLine(" Time Elapsed: {Time}ms", sw.ElapsedMilliseconds); + } + catch (OperationCanceledException) + { + sw.Stop(); + + this.WriteLineError("Connection timed out after {Timeout}", timeout); + this.WriteLine(); + this.WriteLine(" Time Elapsed: {Time}ms", sw.ElapsedMilliseconds); + } + catch (Exception ex) + { + sw.Stop(); + + this.WriteLineError("Error: {Error}", ex.Message); + + if (this.Verbose) + { + this.WriteLine(1, "{StackTrace}", ex.StackTrace); + } + } + + this.WriteLine(); + } + } +} diff --git a/Bruce/CommandLine/KerberosTokenDecodeCommand.cs b/Bruce/CommandLine/KerberosTokenDecodeCommand.cs new file mode 100644 index 00000000..4a75fa28 --- /dev/null +++ b/Bruce/CommandLine/KerberosTokenDecodeCommand.cs @@ -0,0 +1,321 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.Asn1; +using System.Threading.Tasks; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.CommandLine +{ + [CommandLineCommand("ktoken|token", Description = "KerberosTokenDecode")] + public class KerberosTokenDecodeCommand : BaseCommand + { + public KerberosTokenDecodeCommand(CommandLineParameters parameters) + : base(parameters) + { + } + + [CommandLineParameter("token", FormalParameter = true, Description = "Token")] + public string Token { get; set; } + + [CommandLineParameter("v|verbose", Description = "Verbose")] + public override bool Verbose { get; set; } + + [CommandLineParameter("raw", Description = "Raw")] + public bool Raw { get; set; } + + public override async Task Execute() + { + if (await base.Execute()) + { + return true; + } + + if (string.IsNullOrWhiteSpace(this.Token)) + { + this.WriteLineError("A base64-encoded token is required"); + return false; + } + + this.WriteLine(); + + var tokenString = this.Token.Trim(); + + // Strip common prefixes + foreach (var prefix in new[] { "Authorization: Negotiate ", "Negotiate ", "Authorization: " }) + { + if (tokenString.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + tokenString = tokenString.Substring(prefix.Length).Trim(); + break; + } + } + + byte[] bytes; + + try + { + bytes = Convert.FromBase64String(tokenString); + } + catch (FormatException) + { + this.WriteLineError("Input is not valid base64"); + return false; + } + + var data = new ReadOnlyMemory(bytes); + + if (this.Raw) + { + this.WriteHeader("Raw Token Bytes"); + this.WriteLine(1, "{Hex}", Hex.DumpHex(bytes)); + this.WriteLine(); + } + + try + { + this.DecodeToken(data); + } + catch (Exception ex) + { + this.WriteLineError("Failed to decode token: {Error}", ex.Message); + + if (this.Verbose) + { + this.WriteLine(1, "{StackTrace}", ex.StackTrace); + } + } + + return true; + } + + private void DecodeToken(ReadOnlyMemory data) + { + // Try SPNEGO first + if (NegotiationToken.CanDecode(data)) + { + var gssToken = GssApiToken.Decode(data); + + var props = new List<(string, object)> + { + (null, "GSS-API Token"), + ("Mechanism", gssToken.ThisMech.Value), + ("Mechanism Name", GetMechName(gssToken.ThisMech.Value)), + }; + + if (gssToken.MessageType != default) + { + props.Add(("Message Type", gssToken.MessageType)); + } + + this.WriteProperties(props); + this.WriteLine(); + + if (gssToken.ThisMech.Value == MechType.SPNEGO || + gssToken.ThisMech.Value == MechType.NEGOEX) + { + this.DecodeSpnego(gssToken.Token); + } + else if (gssToken.Token.Length > 0) + { + this.DecodeKerberosMessage(gssToken.Token); + } + + return; + } + + // Try raw Kerberos + if (KrbApReq.CanDecode(data)) + { + this.DecodeApReq(data); + return; + } + + if (KrbApRep.CanDecode(data)) + { + this.DecodeApRep(data); + return; + } + + if (KrbError.CanDecode(data)) + { + this.DecodeKrbError(data); + return; + } + + this.WriteLineWarning("Unable to determine token type"); + } + + private void DecodeSpnego(ReadOnlyMemory data) + { + var negToken = NegotiationToken.Decode(data); + + if (negToken.InitialToken != null) + { + var init = negToken.InitialToken; + + this.WriteHeader("SPNEGO NegTokenInit"); + + if (init.MechTypes?.Length > 0) + { + this.WriteLine(1, "Mechanism Types:"); + + foreach (var mech in init.MechTypes) + { + this.WriteLine(2, " {Mech} ({MechName})", mech.Value, GetMechName(mech.Value)); + } + + this.WriteLine(); + } + + if (init.MechToken.HasValue && init.MechToken.Value.Length > 0) + { + this.WriteHeader("Inner MechToken"); + this.DecodeKerberosMessage(init.MechToken.Value); + } + } + + if (negToken.ResponseToken != null) + { + var resp = negToken.ResponseToken; + + var props = new List<(string, object)> + { + (null, "SPNEGO NegTokenResp"), + ("State", resp.State), + }; + + if (resp.SupportedMech != null) + { + props.Add(("Supported Mech", $"{resp.SupportedMech.Value} ({GetMechName(resp.SupportedMech.Value)})")); + } + + this.WriteProperties(props); + this.WriteLine(); + + if (resp.ResponseToken.HasValue && resp.ResponseToken.Value.Length > 0) + { + this.WriteHeader("Inner ResponseToken"); + this.DecodeKerberosMessage(resp.ResponseToken.Value); + } + } + } + + private void DecodeKerberosMessage(ReadOnlyMemory data) + { + if (KrbApReq.CanDecode(data)) + { + this.DecodeApReq(data); + } + else if (KrbApRep.CanDecode(data)) + { + this.DecodeApRep(data); + } + else if (KrbError.CanDecode(data)) + { + this.DecodeKrbError(data); + } + else + { + this.WriteLineWarning("Inner token is not a recognized Kerberos message type"); + + if (this.Verbose) + { + this.WriteLine(1, "{Hex}", Hex.DumpHex(data.ToArray())); + } + } + } + + private void DecodeApReq(ReadOnlyMemory data) + { + var apReq = KrbApReq.DecodeApplication(data); + + var props = new List<(string, object)> + { + (null, "AP-REQ"), + ("Protocol Version", apReq.ProtocolVersionNumber), + ("Message Type", apReq.MessageType), + ("AP Options", apReq.ApOptions), + }; + + this.WriteProperties(props); + this.WriteLine(); + + var ticket = apReq.Ticket; + + var ticketProps = new List<(string, object)> + { + (null, "Ticket"), + ("Ticket Version", ticket.TicketNumber), + ("Realm", ticket.Realm), + ("Server Name", ticket.SName?.FullyQualifiedName), + ("Encryption Type", ticket.EncryptedPart.EType), + ("Key Version", ticket.EncryptedPart.KeyVersionNumber), + }; + + this.WriteProperties(ticketProps); + this.WriteLine(); + + if (this.Verbose) + { + this.WriteLine(1, "Authenticator EType: {EType}", apReq.Authenticator.EType); + } + } + + private void DecodeApRep(ReadOnlyMemory data) + { + var apRep = KrbApRep.DecodeApplication(data); + + var props = new List<(string, object)> + { + (null, "AP-REP"), + ("Protocol Version", apRep.ProtocolVersionNumber), + ("Message Type", apRep.MessageType), + ("Encryption Type", apRep.EncryptedPart.EType), + }; + + this.WriteProperties(props); + } + + private void DecodeKrbError(ReadOnlyMemory data) + { + var error = KrbError.DecodeApplication(data); + + var props = new List<(string, object)> + { + (null, "KRB-ERROR"), + ("Error Code", error.ErrorCode), + ("Error Text", error.EText), + ("Server Time", error.STime), + ("Client Time", error.CTime), + ("Realm", error.Realm), + ("Server Name", error.SName?.FullyQualifiedName), + ("Client Name", error.CName?.FullyQualifiedName), + }; + + this.WriteProperties(props); + } + + private static string GetMechName(string oid) + { + return oid switch + { + MechType.SPNEGO => "SPNEGO", + MechType.NEGOEX => "NEGOEX", + MechType.KerberosGssApi => "Kerberos 5", + MechType.KerberosV5Legacy => "Kerberos 5 (Legacy)", + MechType.KerberosUser2User => "Kerberos User2User", + MechType.IAKerb => "IAKerb", + MechType.NTLM => "NTLM", + _ => "Unknown" + }; + } + } +} diff --git a/Bruce/CommandLine/KerberosWhoAmICommand.cs b/Bruce/CommandLine/KerberosWhoAmICommand.cs index e0d570de..6b93bf49 100644 --- a/Bruce/CommandLine/KerberosWhoAmICommand.cs +++ b/Bruce/CommandLine/KerberosWhoAmICommand.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // Licensed to The .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // ----------------------------------------------------------------------- @@ -40,6 +40,12 @@ public KerberosWhoAmI(CommandLineParameters parameters) [CommandLineParameter("claims", Description = "Claims")] public bool Claims { get; set; } + [CommandLineParameter("delegation", Description = "Delegation")] + public bool Delegation { get; set; } + + [CommandLineParameter("signatures", Description = "Signatures")] + public bool Signatures { get; set; } + public override async Task Execute() { if (await base.Execute()) @@ -122,15 +128,28 @@ internal void DescribeTicket(KerberosIdentity identity) (SR.Resource("CommandLine_WhoAmI_UserName"), $"{identity.Name}"), }; + if (pac != null) + { + properties.Add(("PAC Version", pac.Version)); + + if (pac.DecodingErrors?.Any() == true) + { + foreach (var err in pac.DecodingErrors) + { + properties.Add(("Decoding Error", $"{err.Type}: {err.Exception?.Message}")); + } + } + } + if (this.All || this.Logon) { var objects = new object[] { - pac.LogonInfo, - pac.ClientInformation, - pac.DelegationInformation, - pac.UpnDomainInformation, - pac.CredentialType + pac?.LogonInfo, + pac?.ClientInformation, + pac?.DelegationInformation, + pac?.UpnDomainInformation, + pac?.CredentialType }; GetObjectProperties(objects, properties); @@ -162,31 +181,74 @@ internal void DescribeTicket(KerberosIdentity identity) this.WriteProperties(properties); + if (this.All || this.Delegation) + { + if (pac?.DelegationInformation != null) + { + this.WriteLine(); + this.WriteHeader("Delegation"); + + var delProps = new List<(string, object)>(); + GetObjectProperties(new object[] { pac.DelegationInformation }, delProps); + this.WriteProperties(delProps); + } + } + + if (this.All || this.Signatures) + { + if (pac != null) + { + this.WriteLine(); + this.WriteHeader("Signatures"); + + if (pac.ServerSignature != null) + { + this.WriteLine(1, " Server Signature Type: {Type}", pac.ServerSignature.Type); + } + + if (pac.KdcSignature != null) + { + this.WriteLine(1, " KDC Signature Type: {Type}", pac.KdcSignature.Type); + } + } + } + if (this.All || this.Groups) { this.WriteLine(); this.WriteHeader(SR.Resource("CommandLine_WhoAmI_Groups")); this.WriteLine(); - var certSids = new List(); - - if (pac.CredentialType != null) + if (pac?.LogonInfo != null) { - certSids.Add(SecurityIdentifier.WellKnown.ThisOrganizationCertificate); - } + var certSids = new List(); - var sids = certSids.Union(pac.LogonInfo.ExtraSids).Union(pac.LogonInfo.GroupSids).Union(pac.LogonInfo.ResourceGroups).Select(s => new - { - Sid = s, - Name = SecurityIdentifierNames.GetFriendlyName(s.Value, pac.LogonInfo.DomainSid.Value) - }); + if (pac.CredentialType != null) + { + certSids.Add(SecurityIdentifier.WellKnown.ThisOrganizationCertificate); + } - var max = sids.Max(s => s.Sid.Value.Length); - var maxName = sids.Max(s => s.Name?.Length ?? 0); + var sids = certSids + .Union(pac.LogonInfo.ExtraSids ?? Enumerable.Empty()) + .Union(pac.LogonInfo.GroupSids ?? Enumerable.Empty()) + .Union(pac.LogonInfo.ResourceGroups ?? Enumerable.Empty()) + .Select(s => new + { + Sid = s, + Name = SecurityIdentifierNames.GetFriendlyName(s.Value, pac.LogonInfo.DomainSid?.Value) + }) + .ToList(); + + if (sids.Any()) + { + var max = sids.Max(s => s.Sid.Value.Length); + var maxName = sids.Max(s => s.Name?.Length ?? 0); - foreach (var group in sids.OrderBy(c => c.Sid.Value)) - { - this.WriteLine(1, string.Format("{0} {1} {{Attr}}", (group.Name ?? "").PadRight(maxName), group.Sid.Value.PadRight(max)), group.Sid.Attributes); + foreach (var group in sids.OrderBy(c => c.Sid.Value)) + { + this.WriteLine(1, string.Format("{0} {1} {{Attr}}", (group.Name ?? "").PadRight(maxName), group.Sid.Value.PadRight(max)), group.Sid.Attributes); + } + } } } } diff --git a/Kerberos.NET/Client/KerberosMessageService.cs b/Kerberos.NET/Client/KerberosMessageService.cs new file mode 100644 index 00000000..79253bf9 --- /dev/null +++ b/Kerberos.NET/Client/KerberosMessageService.cs @@ -0,0 +1,95 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.Client +{ + /// + /// Provides application-level message protection using the Kerberos KRB-SAFE + /// (integrity protection) and KRB-PRIV (confidentiality protection) message types + /// as defined in RFC 4120 sections 3.4 and 3.5. + /// + public class KerberosMessageService + { + private readonly KerberosKey sessionKey; + private readonly KrbHostAddress localAddress; + private readonly KrbHostAddress remoteAddress; + private int sequenceNumber; + + public KerberosMessageService( + KerberosKey sessionKey, + KrbHostAddress localAddress = null, + KrbHostAddress remoteAddress = null, + int initialSequenceNumber = 0) + { + this.sessionKey = sessionKey ?? throw new ArgumentNullException(nameof(sessionKey)); + this.localAddress = localAddress; + this.remoteAddress = remoteAddress; + this.sequenceNumber = initialSequenceNumber; + } + + /// + /// Create a KRB-SAFE message providing integrity protection for the data. + /// + public KrbSafe MakeSafe(ReadOnlyMemory data) + { + return KrbSafe.Create( + data, + this.sessionKey, + this.localAddress, + this.remoteAddress, + this.sequenceNumber++); + } + + /// + /// Verify a KRB-SAFE message and return the protected data. + /// + public ReadOnlyMemory VerifySafe(KrbSafe safe) + { + if (safe == null) + { + throw new ArgumentNullException(nameof(safe)); + } + + return safe.Verify(this.sessionKey); + } + + /// + /// Create a KRB-PRIV message providing confidentiality protection for the data. + /// + public KrbPriv MakePriv(ReadOnlyMemory data) + { + var privPart = new KrbEncKrbPrivPart + { + UserData = data, + Timestamp = DateTimeOffset.UtcNow, + Usec = 0, + SeqNumber = this.sequenceNumber++, + SAddress = this.localAddress, + RAddress = this.remoteAddress + }; + + return KrbPriv.Create(this.sessionKey, privPart); + } + + /// + /// Decrypt a KRB-PRIV message and return the protected data. + /// + public ReadOnlyMemory DecryptPriv(KrbPriv priv) + { + if (priv == null) + { + throw new ArgumentNullException(nameof(priv)); + } + + var decrypted = priv.Decrypt(this.sessionKey); + + return decrypted.UserData; + } + } +} diff --git a/Kerberos.NET/Crypto/KeyUsage.cs b/Kerberos.NET/Crypto/KeyUsage.cs index 83a18d21..67699614 100644 --- a/Kerberos.NET/Crypto/KeyUsage.cs +++ b/Kerberos.NET/Crypto/KeyUsage.cs @@ -44,6 +44,7 @@ public enum KeyUsage SamEncNonceSad = 27, PaPkInitEx = 44, AsReq = 56, + CammacChecksum = 64, FastReqChecksum = 50, FastEnc = 51, FastRep = 52, diff --git a/Kerberos.NET/Crypto/Spake/SpakeExchange.cs b/Kerberos.NET/Crypto/Spake/SpakeExchange.cs new file mode 100644 index 00000000..94f8a4e3 --- /dev/null +++ b/Kerberos.NET/Crypto/Spake/SpakeExchange.cs @@ -0,0 +1,106 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Security.Cryptography; +using Kerberos.NET.Configuration; + +namespace Kerberos.NET.Crypto +{ + /// + /// Implements the SPAKE2+ password-authenticated key exchange for Kerberos + /// pre-authentication per draft-ietf-kitten-krb-spake-preauth. + /// + public static class SpakeExchange + { + /// + /// Get the key size in bytes for the given SPAKE group. + /// + public static int GetKeySize(SpakePreAuthGroupType group) + { + return group switch + { + SpakePreAuthGroupType.Edwards25519 => 32, + SpakePreAuthGroupType.P_256 => 32, + SpakePreAuthGroupType.P_384 => 48, + SpakePreAuthGroupType.P_521 => 66, + _ => throw new ArgumentOutOfRangeException(nameof(group)) + }; + } + + /// + /// Get the hash algorithm for the given SPAKE group. + /// + public static HashAlgorithmName GetHashAlgorithm(SpakePreAuthGroupType group) + { + return group switch + { + SpakePreAuthGroupType.Edwards25519 => HashAlgorithmName.SHA256, + SpakePreAuthGroupType.P_256 => HashAlgorithmName.SHA256, + SpakePreAuthGroupType.P_384 => HashAlgorithmName.SHA384, + SpakePreAuthGroupType.P_521 => HashAlgorithmName.SHA512, + _ => throw new ArgumentOutOfRangeException(nameof(group)) + }; + } + + /// + /// Generate a random scalar value for the SPAKE exchange. + /// + public static byte[] GenerateScalar(SpakePreAuthGroupType group) + { + var keySize = GetKeySize(group); + var scalar = new byte[keySize]; + + using var rng = RandomNumberGenerator.Create(); + rng.GetBytes(scalar); + + return scalar; + } + + /// + /// Derive the SPAKE pre-authentication key from the shared secret + /// using the transcript hash per the draft specification. + /// + public static byte[] DeriveKey( + SpakePreAuthGroupType group, + byte[] sharedSecret, + byte[] transcript) + { + if (sharedSecret == null) + { + throw new ArgumentNullException(nameof(sharedSecret)); + } + + if (transcript == null) + { + throw new ArgumentNullException(nameof(transcript)); + } + + var hashAlg = GetHashAlgorithm(group); + var keySize = GetKeySize(group); + + // K' = HMAC(transcript_hash, shared_secret) + // The actual key derivation is: K = KDF(K', "SPAKEKey") + + using var hash = IncrementalHash.CreateHash(hashAlg); + hash.AppendData(transcript); + var transcriptHash = hash.GetHashAndReset(); + + using var hmac = IncrementalHash.CreateHMAC(hashAlg, transcriptHash); + hmac.AppendData(sharedSecret); + var derived = hmac.GetHashAndReset(); + + // Truncate to key size if needed + if (derived.Length > keySize) + { + var truncated = new byte[keySize]; + Buffer.BlockCopy(derived, 0, truncated, 0, keySize); + return truncated; + } + + return derived; + } + } +} diff --git a/Kerberos.NET/Entities/KerberosConstants.cs b/Kerberos.NET/Entities/KerberosConstants.cs index a1ee0d62..6ece5adc 100644 --- a/Kerberos.NET/Entities/KerberosConstants.cs +++ b/Kerberos.NET/Entities/KerberosConstants.cs @@ -15,6 +15,8 @@ namespace Kerberos.NET.Entities { internal static class KerberosConstants { + public const string AnonymousRealm = "WELLKNOWN:ANONYMOUS"; + private const int TickUSec = 1000000; private static readonly RandomNumberGenerator Rng = RandomNumberGenerator.Create(); diff --git a/Kerberos.NET/Entities/Krb/AuthorizationDataType.cs b/Kerberos.NET/Entities/Krb/AuthorizationDataType.cs index 35c90882..9757c481 100644 --- a/Kerberos.NET/Entities/Krb/AuthorizationDataType.cs +++ b/Kerberos.NET/Entities/Krb/AuthorizationDataType.cs @@ -23,6 +23,12 @@ public enum AuthorizationDataType KerbAuthDataTokenRestrictions = 141, KerbLocal = 142, KerbApOptions = 143, - KerbServiceTarget = 144 + KerbServiceTarget = 144, + + /// + /// Container Authenticated by Multiple MACs (CAMMAC) per RFC 7751. + /// Wraps authorization data with KDC and service verifier MACs. + /// + AdCammac = 96 } } \ No newline at end of file diff --git a/Kerberos.NET/Entities/Krb/CammacAuthorizationData.cs b/Kerberos.NET/Entities/Krb/CammacAuthorizationData.cs new file mode 100644 index 00000000..6986f9dd --- /dev/null +++ b/Kerberos.NET/Entities/Krb/CammacAuthorizationData.cs @@ -0,0 +1,152 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Crypto; + +namespace Kerberos.NET.Entities +{ + /// + /// Containers Authenticated by Multiple MACs per RFC 7751. + /// Provides integrity protection for authorization data across + /// realm boundaries using KDC and service verifier MACs. + /// Encode/Decode methods are provided by the generated partial class. + /// + public partial class KrbAdCammac + { + /// + /// Create a CAMMAC wrapping the given authorization data with KDC and service verifiers. + /// + /// The authorization data elements to protect. + /// The KDC key used to create the KDC verifier MAC. + /// Optional service key used to create the service verifier MAC. + /// A new CAMMAC instance with computed verifier MACs. + public static KrbAdCammac Create( + KrbAuthorizationData[] elements, + KerberosKey kdcKey, + KerberosKey serviceKey = null) + { + if (elements == null) + { + throw new ArgumentNullException(nameof(elements)); + } + + if (kdcKey == null) + { + throw new ArgumentNullException(nameof(kdcKey)); + } + + var cammac = new KrbAdCammac + { + Elements = elements + }; + + // Encode the elements for MAC computation + var encodedElements = EncodeElements(elements); + + // KDC verifier using the KDC key + cammac.KdcVerifier = CreateVerifier(encodedElements, kdcKey); + + // Optional service verifier + if (serviceKey != null) + { + cammac.ServiceVerifier = CreateVerifier(encodedElements, serviceKey); + } + + return cammac; + } + + /// + /// Validate the KDC verifier MAC. + /// + /// The KDC key to validate against. + /// True if the KDC verifier is valid; otherwise false. + public bool ValidateKdcVerifier(KerberosKey kdcKey) + { + if (this.KdcVerifier?.Mac == null) + { + return false; + } + + return ValidateVerifier(this.Elements, this.KdcVerifier, kdcKey); + } + + /// + /// Validate the service verifier MAC. + /// + /// The service key to validate against. + /// True if the service verifier is valid; otherwise false. + public bool ValidateServiceVerifier(KerberosKey serviceKey) + { + if (this.ServiceVerifier?.Mac == null) + { + return false; + } + + return ValidateVerifier(this.Elements, this.ServiceVerifier, serviceKey); + } + + /// + /// Wrap this CAMMAC as a element with type . + /// + public KrbAuthorizationData ToAuthorizationData() + { + return new KrbAuthorizationData + { + Type = AuthorizationDataType.AdCammac, + Data = this.Encode() + }; + } + + private static KrbVerifierMac CreateVerifier(ReadOnlyMemory encodedElements, KerberosKey key) + { + var checksum = KrbChecksum.Create(encodedElements, key, KeyUsage.CammacChecksum); + + return new KrbVerifierMac + { + Mac = checksum, + EncryptionType = key.EncryptionType + }; + } + + private static bool ValidateVerifier(KrbAuthorizationData[] elements, KrbVerifierMac verifier, KerberosKey key) + { + try + { + var encodedElements = EncodeElements(elements); + + var validator = CryptoService.CreateChecksum( + verifier.Mac.Type, + signature: verifier.Mac.Checksum, + signatureData: encodedElements + ); + + if (validator == null) + { + return false; + } + + validator.Usage = KeyUsage.CammacChecksum; + validator.Validate(key); + + return true; + } + catch + { + return false; + } + } + + private static ReadOnlyMemory EncodeElements(KrbAuthorizationData[] elements) + { + var sequence = new KrbAuthorizationDataSequence + { + AuthorizationData = elements + }; + + return sequence.Encode(); + } + } +} diff --git a/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs b/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs index 0539fd64..3d8aee63 100644 --- a/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs +++ b/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs @@ -93,7 +93,10 @@ public partial class KrbPrincipalName "@", // 8 "@", // 9 "@", // NT_ENTERPRISE = 10, - "/" // NT_WELLKNOWN = 11 + "/", // NT_WELLKNOWN = 11 + "@", // 12 + "@", // 13 + "@" // NT_ANONYMOUS = 14 }; internal PrincipalName ToKeyPrincipal() @@ -379,6 +382,12 @@ public static class WellKnown { public static KrbPrincipalName Krbtgt(string realm = null) => FromString(KrbtgtService, PrincipalNameType.NT_SRV_INST, realm); + + public static KrbPrincipalName Anonymous() => new KrbPrincipalName + { + Type = PrincipalNameType.NT_WELLKNOWN, + Name = new[] { "WELLKNOWN", "ANONYMOUS" } + }; } } } diff --git a/Kerberos.NET/Entities/Krb/KrbPriv.cs b/Kerberos.NET/Entities/Krb/KrbPriv.cs index be13ce05..18c0262a 100644 --- a/Kerberos.NET/Entities/Krb/KrbPriv.cs +++ b/Kerberos.NET/Entities/Krb/KrbPriv.cs @@ -1,4 +1,10 @@ -using Kerberos.NET.Crypto; +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Crypto; namespace Kerberos.NET.Entities { @@ -16,5 +22,22 @@ public static KrbPriv Create(KerberosKey key, KrbEncKrbPrivPart krbPrivEncPartUn usage: KeyUsage.EncKrbPrivPart) }; } + + /// + /// Decrypt the KRB-PRIV message and return the enclosed private part. + /// + public KrbEncKrbPrivPart Decrypt(KerberosKey key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + return this.EncPart.Decrypt( + key, + KeyUsage.EncKrbPrivPart, + b => KrbEncKrbPrivPart.DecodeApplication(b) + ); + } } } diff --git a/Kerberos.NET/Entities/Krb/KrbSafe.cs b/Kerberos.NET/Entities/Krb/KrbSafe.cs new file mode 100644 index 00000000..d9a72a9c --- /dev/null +++ b/Kerberos.NET/Entities/Krb/KrbSafe.cs @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Crypto; + +namespace Kerberos.NET.Entities +{ + public partial class KrbSafe + { + /// + /// Create a KRB-SAFE message that integrity-protects the provided data. + /// + public static KrbSafe Create( + ReadOnlyMemory userData, + KerberosKey key, + KrbHostAddress senderAddress, + KrbHostAddress recipientAddress = null, + int? sequenceNumber = null) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + var body = new KrbSafeBody + { + UserData = userData, + Timestamp = DateTimeOffset.UtcNow, + Usec = 0, + SeqNumber = sequenceNumber, + SAddress = senderAddress, + RAddress = recipientAddress + }; + + var encodedBody = body.Encode(); + + var checksum = KrbChecksum.Create(encodedBody, key, KeyUsage.KrbSafeChecksum); + + return new KrbSafe + { + ProtocolVersionNumber = 5, + MessageType = MessageType.KRB_SAFE, + SafeBody = body, + Checksum = checksum + }; + } + + /// + /// Verify the integrity of a KRB-SAFE message and return the user data. + /// + public ReadOnlyMemory Verify(KerberosKey key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (this.SafeBody == null) + { + throw new InvalidOperationException("SafeBody is null"); + } + + var encodedBody = this.SafeBody.Encode(); + + var checksumValidator = CryptoService.CreateChecksum( + this.Checksum.Type, + this.Checksum.Checksum, + encodedBody + ); + + checksumValidator.Usage = KeyUsage.KrbSafeChecksum; + checksumValidator.Validate(key); + + return this.SafeBody.UserData; + } + } +} diff --git a/Kerberos.NET/Entities/Krb/PaDataType.cs b/Kerberos.NET/Entities/Krb/PaDataType.cs index 91fc32e3..ca2b351f 100644 --- a/Kerberos.NET/Entities/Krb/PaDataType.cs +++ b/Kerberos.NET/Entities/Krb/PaDataType.cs @@ -316,6 +316,17 @@ public enum PaDataType /// PA_REQ_ENC_PA_REP = 149, + /// + /// The PA-Data contains a freshness token for PKINIT, preventing pre-play attacks. + /// + PA_AS_FRESHNESS = 150, + + /// + /// The PA-Data contains SPAKE pre-authentication data for + /// password-authenticated key exchange. + /// + PA_SPAKE = 151, + /// /// The PA-Data contains a PA-SUPPORTED-ENCTYPES structure /// which specifies the encryption types supported and contains diff --git a/Kerberos.NET/Entities/Krb/VerifierMac.cs b/Kerberos.NET/Entities/Krb/VerifierMac.cs new file mode 100644 index 00000000..b7cae4f1 --- /dev/null +++ b/Kerberos.NET/Entities/Krb/VerifierMac.cs @@ -0,0 +1,16 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Entities +{ + /// + /// Verifier-MAC structure per RFC 7751 Section 3.2. Contains a MAC checksum + /// with optional principal identifier, key version, and encryption type. + /// Encode/Decode methods are provided by the generated partial class. + /// + public partial class KrbVerifierMac + { + } +} diff --git a/Kerberos.NET/Entities/Pac/PacAttributesInfo.cs b/Kerberos.NET/Entities/Pac/PacAttributesInfo.cs new file mode 100644 index 00000000..d2544d9e --- /dev/null +++ b/Kerberos.NET/Entities/Pac/PacAttributesInfo.cs @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Entities.Pac; +using Kerberos.NET.Ndr; + +namespace Kerberos.NET.Entities +{ + [Flags] + public enum PacAttributeFlags : int + { + None = 0, + PacWasRequested = 1, + PacWasGivenImplicitly = 2 + } + + public class PacAttributesInfo : PacObject + { + public int FlagsLength { get; set; } = 32; + + public PacAttributeFlags Flags { get; set; } + + public override PacType PacType => PacType.ATTRIBUTES_INFO; + + public override ReadOnlyMemory Marshal() + { + using (var buffer = new NdrBuffer()) + { + buffer.WriteInt32LittleEndian(this.FlagsLength); + buffer.WriteInt32LittleEndian((int)this.Flags); + + return buffer.ToMemory(); + } + } + + public override void Unmarshal(ReadOnlyMemory bytes) + { + using (var buffer = new NdrBuffer(bytes)) + { + this.FlagsLength = buffer.ReadInt32LittleEndian(); + this.Flags = (PacAttributeFlags)buffer.ReadInt32LittleEndian(); + } + } + } +} diff --git a/Kerberos.NET/Entities/Pac/PacDeviceInfo.cs b/Kerberos.NET/Entities/Pac/PacDeviceInfo.cs new file mode 100644 index 00000000..2715ef1b --- /dev/null +++ b/Kerberos.NET/Entities/Pac/PacDeviceInfo.cs @@ -0,0 +1,12 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Entities.Pac +{ + public class PacDeviceInfo : PacLogonInfo + { + public override PacType PacType => PacType.DEVICE_INFO; + } +} diff --git a/Kerberos.NET/Entities/Pac/PacRequestor.cs b/Kerberos.NET/Entities/Pac/PacRequestor.cs new file mode 100644 index 00000000..e54373f6 --- /dev/null +++ b/Kerberos.NET/Entities/Pac/PacRequestor.cs @@ -0,0 +1,57 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Entities.Pac; +using Kerberos.NET.Ndr; + +namespace Kerberos.NET.Entities +{ + public class PacRequestor : PacObject + { + public SecurityIdentifier RequestorSid { get; set; } + + public override PacType PacType => PacType.REQUESTOR; + + public override ReadOnlyMemory Marshal() + { + using (var buffer = new NdrBuffer()) + { + var rpcSid = this.RequestorSid.ToRpcSid(); + + buffer.WriteByte(rpcSid.Revision); + buffer.WriteByte(rpcSid.SubAuthorityCount); + buffer.WriteSpan(rpcSid.IdentifierAuthority.IdentifierAuthority.Span); + buffer.WriteFixedPrimitiveArray(rpcSid.SubAuthority.Span); + + return buffer.ToMemory(); + } + } + + public override void Unmarshal(ReadOnlyMemory bytes) + { + using (var buffer = new NdrBuffer(bytes)) + { + var revision = buffer.ReadByteLittleEndian(); + var subAuthorityCount = buffer.ReadByteLittleEndian(); + + var identifierAuthority = new RpcSidIdentifierAuthority(); + identifierAuthority.Unmarshal(buffer); + + var subAuthorities = buffer.ReadFixedPrimitiveArray(subAuthorityCount).ToArray(); + + var rpcSid = new RpcSid + { + Revision = revision, + SubAuthorityCount = subAuthorityCount, + IdentifierAuthority = identifierAuthority, + SubAuthority = subAuthorities + }; + + this.RequestorSid = rpcSid.ToSecurityIdentifier(); + } + } + } +} diff --git a/Kerberos.NET/Entities/Pac/PacType.cs b/Kerberos.NET/Entities/Pac/PacType.cs index e76e562c..fb6031e7 100644 --- a/Kerberos.NET/Entities/Pac/PacType.cs +++ b/Kerberos.NET/Entities/Pac/PacType.cs @@ -19,6 +19,11 @@ public enum PacType UPN_DOMAIN_INFO = 12, CLIENT_CLAIMS = 13, DEVICE_INFO = 14, - DEVICE_CLAIMS = 15 + DEVICE_CLAIMS = 15, + + TICKET_CHECKSUM = 0x10, + ATTRIBUTES_INFO = 0x11, + REQUESTOR = 0x12, + FULL_CHECKSUM = 0x13 } } \ No newline at end of file diff --git a/Kerberos.NET/Entities/Pac/PrivilegedAttributeCertificate.cs b/Kerberos.NET/Entities/Pac/PrivilegedAttributeCertificate.cs index d8c482d2..d2e38f35 100644 --- a/Kerberos.NET/Entities/Pac/PrivilegedAttributeCertificate.cs +++ b/Kerberos.NET/Entities/Pac/PrivilegedAttributeCertificate.cs @@ -118,8 +118,13 @@ public static void RegisterType(PacType pacType, Type type) { PacType.UPN_DOMAIN_INFO, typeof(UpnDomainInfo) }, { PacType.CLIENT_CLAIMS, typeof(ClaimsSetMetadata) }, - // { PacType.DEVICE_INFO, typeof(PacLogonInfo) }, + { PacType.DEVICE_INFO, typeof(PacDeviceInfo) }, { PacType.DEVICE_CLAIMS, typeof(ClaimsSetMetadata) }, + + { PacType.TICKET_CHECKSUM, typeof(PacSignature) }, + { PacType.ATTRIBUTES_INFO, typeof(PacAttributesInfo) }, + { PacType.REQUESTOR, typeof(PacRequestor) }, + { PacType.FULL_CHECKSUM, typeof(PacSignature) }, }; private readonly Dictionary attributes = new Dictionary(); @@ -278,6 +283,51 @@ public PacDelegationInfo DelegationInformation set => this.Attributes[PacType.CONSTRAINED_DELEGATION_INFO] = value; } + /// + /// Contains the KERB_VALIDATION_INFO for the device, using the same structure as LOGON_INFO. + /// + public PacDeviceInfo DeviceInfo + { + get => this.GetAttribute(PacType.DEVICE_INFO); + set => this.Attributes[PacType.DEVICE_INFO] = value; + } + + /// + /// Contains the signature of the ticket encrypted part, signed using the KDC key. + /// + public PacSignature TicketSignature + { + get => this.GetAttribute(PacType.TICKET_CHECKSUM); + set => this.Attributes[PacType.TICKET_CHECKSUM] = value; + } + + /// + /// Contains attributes of the PAC such as whether it was requested or given implicitly. + /// + public PacAttributesInfo AttributesInfo + { + get => this.GetAttribute(PacType.ATTRIBUTES_INFO); + set => this.Attributes[PacType.ATTRIBUTES_INFO] = value; + } + + /// + /// Contains the SID of the principal that requested the ticket. + /// + public PacRequestor Requestor + { + get => this.GetAttribute(PacType.REQUESTOR); + set => this.Attributes[PacType.REQUESTOR] = value; + } + + /// + /// Contains the full PAC checksum signed using the KDC key. + /// + public PacSignature FullKdcSignature + { + get => this.GetAttribute(PacType.FULL_CHECKSUM); + set => this.Attributes[PacType.FULL_CHECKSUM] = value; + } + /// /// Indicates whether this PAC contains enough of the required fields to be included in the ticket. /// @@ -357,6 +407,20 @@ private static void SignPac(IEnumerable pacElements, Memory pac { element.Sign(serverSignature.Signature, kdcKey); } + + if (element.PacType == PacType.TICKET_CHECKSUM) + { + // Ticket checksum signs the ticket itself, not the PAC. + // Since we don't have the ticket at PAC generation time, + // sign over the PAC data using the KDC key. + element.Sign(pacUnsigned, kdcKey); + } + + if (element.PacType == PacType.FULL_CHECKSUM) + { + // Full checksum signs the entire PAC using the KDC key. + element.Sign(pacUnsigned, kdcKey); + } } } @@ -440,6 +504,8 @@ private IEnumerable CollectElements(KerberosKey kdcKey, KerberosKey s this.ServerSignature = new PacSignature(PacType.SERVER_CHECKSUM, serverKey.EncryptionType); this.KdcSignature = new PacSignature(PacType.PRIVILEGE_SERVER_CHECKSUM, kdcKey.EncryptionType); + this.TicketSignature = new PacSignature(PacType.TICKET_CHECKSUM, kdcKey.EncryptionType); + this.FullKdcSignature = new PacSignature(PacType.FULL_CHECKSUM, kdcKey.EncryptionType); foreach (var kv in this.Attributes) { diff --git a/Kerberos.NET/Entities/PrincipalNameType.cs b/Kerberos.NET/Entities/PrincipalNameType.cs index 5a8843c2..9579c90a 100644 --- a/Kerberos.NET/Entities/PrincipalNameType.cs +++ b/Kerberos.NET/Entities/PrincipalNameType.cs @@ -59,5 +59,10 @@ public enum PrincipalNameType /// Represents a name that is considered well-known or special meaning other than identifying a particular instance. /// NT_WELLKNOWN = 11, + + /// + /// Represents the well-known anonymous principal used for anonymous PKINIT. + /// + NT_ANONYMOUS = 14, } } \ No newline at end of file diff --git a/Kerberos.NET/Kerberos.NET.csproj b/Kerberos.NET/Kerberos.NET.csproj index cafe2b33..da27c2e4 100644 --- a/Kerberos.NET/Kerberos.NET.csproj +++ b/Kerberos.NET/Kerberos.NET.csproj @@ -31,6 +31,11 @@ + + + + + message, KdcServerOptions opt this.PostProcessAuthHandlers[PaDataType.PA_ETYPE_INFO2] = service => new PaDataETypeInfo2Handler(service); + this.PreAuthHandlers[PaDataType.PA_SPAKE] = service => new PaDataSpakeHandler(service); + this.RegisterPreAuthHandlers(this.PostProcessAuthHandlers); + + // Register freshness after RegisterPreAuthHandlers so it only runs + // during post-processing and doesn't add state during pre-auth + this.PostProcessAuthHandlers[PaDataType.PA_AS_FRESHNESS] = service => new PaDataFreshnessHandler(service); } protected override MessageType MessageType => MessageType.KRB_AS_REQ; @@ -224,6 +231,14 @@ private ReadOnlyMemory GenerateAsRep(KrbAsReq asReq, PreAuthenticationCont } + if (asReq.Body.KdcOptions.HasFlag(KdcOptions.RequestAnonymous)) + { + rst.Flags |= TicketFlags.Anonymous; + rst.Flags &= ~(TicketFlags.Forwardable | TicketFlags.Proxiable | TicketFlags.Renewable); + rst.ClientName = KrbPrincipalName.WellKnown.Anonymous(); + rst.ClientRealmName = KerberosConstants.AnonymousRealm; + } + if (rst.EncryptedPartKey == null) { rst.EncryptedPartKey = rst.Principal.RetrieveLongTermCredential(); diff --git a/Kerberos.NET/Server/KdcTgsReqMessageHandler.cs b/Kerberos.NET/Server/KdcTgsReqMessageHandler.cs index ded21031..66bc17a4 100644 --- a/Kerberos.NET/Server/KdcTgsReqMessageHandler.cs +++ b/Kerberos.NET/Server/KdcTgsReqMessageHandler.cs @@ -229,6 +229,26 @@ public override ReadOnlyMemory ExecuteCore(PreAuthenticationContext contex this.EvaluateSecurityPolicy(context.Principal, context.ServicePrincipal); + // Check for S4U2Proxy with Resource-Based Constrained Delegation + bool isRbcdRequest = false; + + if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.ConstrainedDelegation) || + tgsReq.Body.KdcOptions.HasFlag(KdcOptions.CNameInAdditionalTicket)) + { + var pacOptionsData = tgsReq.PaData?.FirstOrDefault(p => p.Type == PaDataType.PA_PAC_OPTIONS); + + if (pacOptionsData != null) + { + var pacOptions = KrbPaPacOptions.Decode(pacOptionsData.Value); + + if (pacOptions.Flags.HasFlag(PacOptions.ResourceBasedConstrainedDelegation)) + { + isRbcdRequest = true; + this.ValidateRbcdRequest(tgsReq, context); + } + } + } + KerberosKey serviceKey; if (tgsReq.Body.KdcOptions.HasFlag(KdcOptions.EncTktInSkey)) @@ -254,6 +274,12 @@ public override ReadOnlyMemory ExecuteCore(PreAuthenticationContext contex flags |= TicketFlags.PreAuthenticated; } + if (isRbcdRequest) + { + // RBCD allows delegation even if the evidence ticket is not forwardable + flags |= TicketFlags.Forwardable; + } + if (context.IncludePac == null) { context.IncludePac = DetectPacRequirement(tgsReq); @@ -389,5 +415,46 @@ IKerberosPrincipal servicePrincipal // good place to check whether the incoming principal is allowed to access the service principal } + + /// + /// Validates a Resource-Based Constrained Delegation (RBCD) request. + /// Verifies that the additional ticket (evidence ticket) is present as required + /// for S4U2Proxy with RBCD. Subclasses can override this method to implement + /// actual RBCD policy checks (e.g., checking msDS-AllowedToActOnBehalfOfOtherIdentity). + /// + /// The TGS request being processed. + /// The pre-authentication context containing principal information. + protected virtual void ValidateRbcdRequest(KrbTgsReq tgsReq, PreAuthenticationContext context) + { + if (tgsReq == null) + { + throw new ArgumentNullException(nameof(tgsReq)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + // Verify the additional ticket (evidence ticket) is present + if (tgsReq.Body.AdditionalTickets == null || tgsReq.Body.AdditionalTickets.Length == 0) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_BADOPTION, + "RBCD requires an additional ticket (evidence ticket)" + ); + } + + // The requesting service must be allowed to delegate to the target service. + // This is determined by the target service principal's policy. + // Subclasses should override this method to implement actual RBCD policy checks + // (e.g., checking msDS-AllowedToActOnBehalfOfOtherIdentity on the target service). + + this.logger.LogInformation( + "RBCD request from {Service} to {Target}", + context.Principal?.PrincipalName, + context.ServicePrincipal?.PrincipalName + ); + } } } diff --git a/Kerberos.NET/Server/PaDataFreshnessHandler.cs b/Kerberos.NET/Server/PaDataFreshnessHandler.cs new file mode 100644 index 00000000..6f4c3997 --- /dev/null +++ b/Kerberos.NET/Server/PaDataFreshnessHandler.cs @@ -0,0 +1,91 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.Server +{ + /// + /// Handles the PA-AS-FRESHNESS pre-authentication type as defined in RFC 8070. + /// This provides a freshness token to PKINIT clients to prevent pre-play attacks. + /// + public class PaDataFreshnessHandler : KdcPreAuthenticationHandlerBase + { + public PaDataFreshnessHandler(IRealmService service) + : base(service) + { + } + + /// + /// After validation, include a PA-AS-FRESHNESS token in the response. + /// The freshness token is an encrypted timestamp using the KDC key, + /// allowing clients to prove their PKINIT request is fresh. + /// + public override void PostValidate(IKerberosPrincipal principal, List preAuthRequirements) + { + if (principal == null) + { + throw new ArgumentNullException(nameof(principal)); + } + + if (preAuthRequirements == null) + { + throw new ArgumentNullException(nameof(preAuthRequirements)); + } + + var krbtgtName = KrbPrincipalName.WellKnown.Krbtgt(this.Service.Name); + var krbtgtPrincipal = this.Service.Principals.Find(krbtgtName, this.Service.Name); + + if (krbtgtPrincipal == null) + { + return; + } + + var kdcKey = krbtgtPrincipal.RetrieveLongTermCredential(); + + var now = this.Service.Now(); + var timestamp = new KrbPaEncTsEnc { PaTimestamp = now, PaUSec = 0 }; + var encrypted = KrbEncryptedData.Encrypt(timestamp.Encode(), kdcKey, KeyUsage.PaEncTs); + + preAuthRequirements.Add(new KrbPaData + { + Type = PaDataType.PA_AS_FRESHNESS, + Value = encrypted.Encode() + }); + } + + /// + /// Validates a freshness token received from a client by decrypting it + /// with the KDC key and verifying the timestamp is within the allowed skew. + /// + /// The encoded freshness token from the client + /// The KDC long-term key used to decrypt the token + /// The maximum allowed clock skew + /// The current time + /// True if the token is valid and within the allowed time skew + public static bool ValidateFreshnessToken( + ReadOnlyMemory tokenData, + KerberosKey kdcKey, + TimeSpan skew, + DateTimeOffset now) + { + try + { + var encrypted = KrbEncryptedData.Decode(tokenData); + var decrypted = encrypted.Decrypt(kdcKey, KeyUsage.PaEncTs, b => KrbPaEncTsEnc.Decode(b)); + + var diff = (now - decrypted.PaTimestamp).Duration(); + return diff <= skew; + } + catch + { + return false; + } + } + } +} diff --git a/Kerberos.NET/Server/PaDataSpakeHandler.cs b/Kerberos.NET/Server/PaDataSpakeHandler.cs new file mode 100644 index 00000000..f153c781 --- /dev/null +++ b/Kerberos.NET/Server/PaDataSpakeHandler.cs @@ -0,0 +1,318 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using Kerberos.NET.Configuration; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; + +namespace Kerberos.NET.Server +{ + /// + /// Handles SPAKE pre-authentication per draft-ietf-kitten-krb-spake-preauth. + /// Provides password-authenticated key exchange that resists offline dictionary attacks. + /// + /// The exchange proceeds in two rounds: + /// 1. Client sends AS-REQ without SPAKE data; KDC returns a challenge with supported groups. + /// 2. Client sends AS-REQ with SPAKE response; KDC completes the exchange and derives the key. + /// + /// The actual elliptic curve operations (point multiplication with blinding factors) are + /// provided as an extensibility point via since the full + /// SPAKE2+ protocol requires EC math that may vary by deployment. + /// + public class PaDataSpakeHandler : KdcPreAuthenticationHandlerBase + { + /// + /// Delegate for performing the SPAKE group-specific elliptic curve computation. + /// Implementations should compute the SPAKE2+ shared secret from the server's + /// private scalar and the client's public element. + /// + /// The negotiated SPAKE group. + /// The server's private scalar value. + /// The client's public element from the SPAKE response. + /// The client's long-term credential for password blinding. + /// The computed shared secret bytes. + public delegate byte[] SpakeGroupOperationDelegate( + SpakePreAuthGroupType group, + byte[] serverPrivateKey, + byte[] clientPublicValue, + KerberosKey password); + + /// + /// Gets or sets the pluggable SPAKE group operation used to compute the shared secret. + /// When null, the handler will use a simplified key derivation that does not perform + /// actual EC point operations. Set this to provide a full SPAKE2+ implementation. + /// + public static SpakeGroupOperationDelegate SpakeGroupOperation { get; set; } + + public PaDataSpakeHandler(IRealmService service) + : base(service) + { + } + + public override void PreValidate(PreAuthenticationContext preauth) + { + if (preauth == null) + { + throw new ArgumentNullException(nameof(preauth)); + } + + // Only initialize SPAKE state if the principal supports it + if (preauth.Principal?.SupportedPreAuthenticationTypes?.Contains(PaDataType.PA_SPAKE) == true) + { + preauth.GetState(PaDataType.PA_SPAKE); + } + } + + public override KrbPaData Validate(KrbKdcReq asReq, PreAuthenticationContext preauth) + { + if (asReq == null) + { + throw new ArgumentNullException(nameof(asReq)); + } + + if (preauth == null) + { + throw new ArgumentNullException(nameof(preauth)); + } + + if (preauth.PreAuthenticationSatisfied) + { + return null; + } + + var spakeState = preauth.GetState(PaDataType.PA_SPAKE); + + // Look for SPAKE PA-Data from the client + var paSpake = asReq.PaData?.FirstOrDefault(p => p.Type == PaDataType.PA_SPAKE); + + if (paSpake == null || paSpake.Value.Length == 0) + { + // First round: no SPAKE data from client, return challenge with supported groups + return GenerateChallenge(spakeState); + } + + // Second round: client sent SPAKE response, complete the exchange + return CompleteExchange(asReq, preauth, spakeState, paSpake.Value); + } + + public override void PostValidate(IKerberosPrincipal principal, List preAuthRequirements) + { + // No post-validation hints needed for SPAKE + } + + private KrbPaData GenerateChallenge(SpakeState spakeState) + { + var configuredGroups = this.Service.Configuration?.Defaults?.SpakePreAuthGroups; + + if (configuredGroups == null || configuredGroups.Count == 0) + { + // Default to P-256 if no groups configured + configuredGroups = new[] { SpakePreAuthGroupType.P_256 }; + } + + // Select the first (highest priority) group + var selectedGroup = configuredGroups.First(); + spakeState.SelectedGroup = selectedGroup; + + // Generate the server's private scalar for this exchange + spakeState.ServerPrivateKey = SpakeExchange.GenerateScalar(selectedGroup); + spakeState.ChallengeSent = true; + + // Encode the challenge: supported group list and server's public value + // The challenge is encoded as: [group_count (2 bytes)] [group_id (2 bytes)]... [server_public_value] + var keySize = SpakeExchange.GetKeySize(selectedGroup); + var groupList = configuredGroups.ToArray(); + var challengeLength = 2 + (groupList.Length * 2) + keySize; + var challenge = new byte[challengeLength]; + + // Group count + challenge[0] = (byte)((groupList.Length >> 8) & 0xFF); + challenge[1] = (byte)(groupList.Length & 0xFF); + + // Group identifiers + for (int i = 0; i < groupList.Length; i++) + { + var groupId = (ushort)groupList[i]; + challenge[2 + (i * 2)] = (byte)((groupId >> 8) & 0xFF); + challenge[2 + (i * 2) + 1] = (byte)(groupId & 0xFF); + } + + // Server's public value (in a full implementation this would be the + // EC public point with password blinding; here we use the raw scalar + // as a placeholder for the extensibility point) + Buffer.BlockCopy( + spakeState.ServerPrivateKey, 0, + challenge, 2 + (groupList.Length * 2), + keySize); + + return new KrbPaData + { + Type = PaDataType.PA_SPAKE, + Value = challenge + }; + } + + private KrbPaData CompleteExchange( + KrbKdcReq asReq, + PreAuthenticationContext preauth, + SpakeState spakeState, + ReadOnlyMemory clientResponse) + { + if (!spakeState.ChallengeSent) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + "SPAKE exchange received a response without a prior challenge" + ); + } + + var principal = preauth.Principal; + var clientKey = principal.RetrieveLongTermCredential(); + + // Parse the client response: [selected_group (2 bytes)] [client_public_value] + var responseBytes = clientResponse.ToArray(); + + if (responseBytes.Length < 2) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + "SPAKE client response is too short" + ); + } + + var selectedGroupId = (ushort)((responseBytes[0] << 8) | responseBytes[1]); + var selectedGroup = (SpakePreAuthGroupType)selectedGroupId; + + // Verify the client selected a group we support + var configuredGroups = this.Service.Configuration?.Defaults?.SpakePreAuthGroups; + + if (configuredGroups == null || !configuredGroups.Contains(selectedGroup)) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + $"SPAKE client selected unsupported group: {selectedGroup}" + ); + } + + var expectedKeySize = SpakeExchange.GetKeySize(selectedGroup); + + if (responseBytes.Length != 2 + expectedKeySize) + { + throw new KerberosProtocolException( + KerberosErrorCode.KDC_ERR_PREAUTH_FAILED, + $"SPAKE client response has invalid length for group {selectedGroup}" + ); + } + + var clientPublicValue = new byte[expectedKeySize]; + Buffer.BlockCopy(responseBytes, 2, clientPublicValue, 0, expectedKeySize); + + // Compute the shared secret using the pluggable group operation + byte[] sharedSecret; + + if (SpakeGroupOperation != null) + { + sharedSecret = SpakeGroupOperation( + selectedGroup, + spakeState.ServerPrivateKey, + clientPublicValue, + clientKey); + } + else + { + // Default simplified key derivation when no EC implementation is plugged in. + // This combines the server private key, client public value, and password + // using HMAC to produce a shared secret. A production deployment should + // provide a full SPAKE2+ group operation via SpakeGroupOperation. + sharedSecret = DeriveSimplifiedSharedSecret( + selectedGroup, + spakeState.ServerPrivateKey, + clientPublicValue, + clientKey); + } + + spakeState.SharedSecret = sharedSecret; + + // Build the transcript for key derivation + // Transcript = H(client_name || server_challenge || client_response) + var clientName = principal.PrincipalName; + var transcript = BuildTranscript(clientName, spakeState.ServerPrivateKey, clientPublicValue); + + // Derive the final key from the shared secret and transcript + var derivedKeyBytes = SpakeExchange.DeriveKey(selectedGroup, sharedSecret, transcript); + + // Use the derived key as the encrypted part key + var preferredEType = clientKey.EncryptionType; + + preauth.EncryptedPartKey = new KerberosKey( + key: derivedKeyBytes, + etype: preferredEType); + preauth.EncryptedPartEType = preferredEType; + preauth.ClientAuthority = PaDataType.PA_SPAKE; + + // Return a confirmation PA-Data containing the server's proof + // In a full implementation this would be EncryptedData proving + // the server also knows the shared secret + var confirmation = new byte[expectedKeySize]; + + using (var hmac = IncrementalHash.CreateHMAC( + SpakeExchange.GetHashAlgorithm(selectedGroup), + derivedKeyBytes)) + { + hmac.AppendData(spakeState.ServerPrivateKey); + hmac.AppendData(clientPublicValue); + var proof = hmac.GetHashAndReset(); + Buffer.BlockCopy(proof, 0, confirmation, 0, Math.Min(proof.Length, confirmation.Length)); + } + + return new KrbPaData + { + Type = PaDataType.PA_SPAKE, + Value = confirmation + }; + } + + private static byte[] DeriveSimplifiedSharedSecret( + SpakePreAuthGroupType group, + byte[] serverPrivateKey, + byte[] clientPublicValue, + KerberosKey password) + { + var hashAlg = SpakeExchange.GetHashAlgorithm(group); + var passwordBytes = password.GetKey().ToArray(); + + using var hmac = IncrementalHash.CreateHMAC(hashAlg, passwordBytes); + hmac.AppendData(serverPrivateKey); + hmac.AppendData(clientPublicValue); + + return hmac.GetHashAndReset(); + } + + private static byte[] BuildTranscript( + string clientName, + byte[] serverPublicValue, + byte[] clientPublicValue) + { + var clientNameBytes = System.Text.Encoding.UTF8.GetBytes(clientName ?? string.Empty); + + var transcript = new byte[clientNameBytes.Length + serverPublicValue.Length + clientPublicValue.Length]; + var offset = 0; + + Buffer.BlockCopy(clientNameBytes, 0, transcript, offset, clientNameBytes.Length); + offset += clientNameBytes.Length; + + Buffer.BlockCopy(serverPublicValue, 0, transcript, offset, serverPublicValue.Length); + offset += serverPublicValue.Length; + + Buffer.BlockCopy(clientPublicValue, 0, transcript, offset, clientPublicValue.Length); + + return transcript; + } + } +} diff --git a/Kerberos.NET/Server/SpakeState.cs b/Kerberos.NET/Server/SpakeState.cs new file mode 100644 index 00000000..88a39ee8 --- /dev/null +++ b/Kerberos.NET/Server/SpakeState.cs @@ -0,0 +1,34 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Server +{ + /// + /// Contains the state information of a SPAKE pre-authentication exchange + /// used between the pre-validation and validation phases. + /// + public class SpakeState : PaDataState + { + /// + /// The SPAKE group selected for this exchange. + /// + public Configuration.SpakePreAuthGroupType SelectedGroup { get; set; } + + /// + /// Indicates the challenge has been sent and we're awaiting a response. + /// + public bool ChallengeSent { get; set; } + + /// + /// The server's SPAKE private scalar value for this exchange. + /// + public byte[] ServerPrivateKey { get; set; } + + /// + /// The derived shared secret from the SPAKE exchange. + /// + public byte[] SharedSecret { get; set; } + } +} diff --git a/Kerberos.NET/kerberos.asn b/Kerberos.NET/kerberos.asn index 0c295d22..9ac151aa 100644 --- a/Kerberos.NET/kerberos.asn +++ b/Kerberos.NET/kerberos.asn @@ -442,6 +442,22 @@ AD-AND-OR ::= SEQUENCE { AD-MANDATORY-FOR-KDC ::= AuthorizationData +-- RFC 7751 CAMMAC + +Verifier-MAC ::= SEQUENCE { + identifier [0] PrincipalName OPTIONAL, + kvno [1] UInt32 OPTIONAL, + enctype [2] Int32 OPTIONAL, + mac [3] Checksum +} + +AD-CAMMAC ::= SEQUENCE { + elements [0] AuthorizationData, + kdc-verifier [1] Verifier-MAC OPTIONAL, + svc-verifier [2] Verifier-MAC OPTIONAL, + other-verifiers [3] SEQUENCE OF Verifier-MAC OPTIONAL +} + END KerberosV5-PK-INIT-SPEC { diff --git a/Kerberos.NET/kerberos.asn.json b/Kerberos.NET/kerberos.asn.json index e56ac2e9..ad1a5d75 100644 --- a/Kerberos.NET/kerberos.asn.json +++ b/Kerberos.NET/kerberos.asn.json @@ -525,6 +525,26 @@ "Fields": { "armored-data": { "CSharpName": "ArmoredData" } } + }, + + // --- CAMMAC (RFC 7751) --- + "Verifier-MAC": { + "CSharpName": "KrbVerifierMac", + "Fields": { + "identifier": { "CSharpName": "Identifier" }, + "kvno": { "CSharpName": "KeyVersionNumber" }, + "enctype": { "CSharpName": "EncryptionType", "EnumType": "EncryptionType", "TreatAsEnum": true }, + "mac": { "CSharpName": "Mac" } + } + }, + "AD-CAMMAC": { + "CSharpName": "KrbAdCammac", + "Fields": { + "elements": { "CSharpName": "Elements" }, + "kdc-verifier": { "CSharpName": "KdcVerifier" }, + "svc-verifier": { "CSharpName": "ServiceVerifier" }, + "other-verifiers": { "CSharpName": "OtherVerifiers" } + } } } } diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosAsn1CommandTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosAsn1CommandTests.cs new file mode 100644 index 00000000..bb043ffb --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosAsn1CommandTests.cs @@ -0,0 +1,267 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using Kerberos.NET.CommandLine; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosAsn1CommandTests : CommandLineTestBase + { + private static InputControl CreateIO(out StringWriter writer) + { + writer = new StringWriter(); + + return new InputControl + { + Clear = () => { }, + HookCtrlC = hook => { }, + ResetColor = () => { }, + SetColor = c => { }, + Writer = writer + }; + } + + [TestMethod] + public void Asn1CommandCanBeCreated() + { + var parameters = CommandLineParameters.Parse("kasn1 dGVzdA=="); + var command = (KerberosAsn1Command)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("dGVzdA==", command.Data); + } + + [TestMethod] + public void Asn1CommandAliasesWork() + { + foreach (var alias in new[] { "asn1", "asn" }) + { + var parameters = CommandLineParameters.Parse($"{alias} dGVzdA=="); + var command = parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsInstanceOfType(command, typeof(KerberosAsn1Command)); + } + } + + [TestMethod] + public async Task Asn1CommandRequiresInput() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kasn1"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + // Should indicate no input + Assert.IsFalse(string.IsNullOrWhiteSpace(output) && output.Length > 200, + "Should produce limited output when no data provided"); + } + + [TestMethod] + public async Task Asn1CommandDecodesBase64DerSequence() + { + // Build a simple KRB-ERROR which is a valid DER structure + var error = new KrbError + { + ErrorCode = KerberosErrorCode.KDC_ERR_PREAUTH_REQUIRED, + Realm = "EXAMPLE.COM", + STime = DateTimeOffset.UtcNow, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", "EXAMPLE.COM" } + } + }; + + var encoded = error.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kasn1 {base64}"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + // Should show ASN.1 structure elements + Assert.IsTrue( + output.Contains("SEQUENCE", StringComparison.OrdinalIgnoreCase) || + output.Contains("APPLICATION", StringComparison.OrdinalIgnoreCase) || + output.Contains("INTEGER", StringComparison.OrdinalIgnoreCase) || + output.Contains("CONTEXT", StringComparison.OrdinalIgnoreCase), + $"Expected ASN.1 tag names in output: {output}" + ); + } + + [TestMethod] + public async Task Asn1CommandDecodesHexInput() + { + // DER encoding of INTEGER 42: 02 01 2A + var hex = "02012A"; + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kasn1 --hex {hex}"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("INTEGER", StringComparison.OrdinalIgnoreCase) || + output.Contains("42"), + $"Expected INTEGER value 42 in output: {output}" + ); + } + + [TestMethod] + public async Task Asn1CommandReadsFromFile() + { + var error = new KrbError + { + ErrorCode = KerberosErrorCode.KDC_ERR_NONE, + Realm = "FILE.TEST", + STime = DateTimeOffset.UtcNow, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { "test" } + } + }; + + var encoded = error.EncodeApplication(); + + using (var tmpFile = new TemporaryFile()) + { + File.WriteAllBytes(tmpFile.File, encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kasn1 --file \"{tmpFile.File}\""); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("SEQUENCE", StringComparison.OrdinalIgnoreCase) || + output.Contains("APPLICATION", StringComparison.OrdinalIgnoreCase), + $"Expected ASN.1 structure from file: {output}" + ); + } + } + + [TestMethod] + public async Task Asn1CommandDepthLimitsOutput() + { + var error = new KrbError + { + ErrorCode = KerberosErrorCode.KDC_ERR_NONE, + Realm = "DEPTH.TEST", + STime = DateTimeOffset.UtcNow, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { "test" } + } + }; + + var encoded = error.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + // Full depth + var ioFull = CreateIO(out var writerFull); + var paramsFull = CommandLineParameters.Parse($"kasn1 {base64}"); + var cmdFull = paramsFull.CreateCommandExecutor(ioFull); + await cmdFull.Execute(); + var fullOutput = writerFull.ToString(); + + // Depth 1 + var ioLimited = CreateIO(out var writerLimited); + var paramsLimited = CommandLineParameters.Parse($"kasn1 --depth 1 {base64}"); + var cmdLimited = paramsLimited.CreateCommandExecutor(ioLimited); + await cmdLimited.Execute(); + var limitedOutput = writerLimited.ToString(); + + // Limited depth should produce less output + Assert.IsTrue( + limitedOutput.Length <= fullOutput.Length, + $"Depth-limited output ({limitedOutput.Length}) should be <= full output ({fullOutput.Length})" + ); + } + + [TestMethod] + public async Task Asn1CommandHandlesInvalidData() + { + var io = CreateIO(out var writer); + // "AAAA" decodes to 3 zero bytes - not valid DER + var parameters = CommandLineParameters.Parse("kasn1 AAAA"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + // Should handle gracefully without crashing + Assert.IsFalse(string.IsNullOrWhiteSpace(output)); + } + + [TestMethod] + public async Task Asn1CommandIdentifiesKerberosApplicationTags() + { + // AS-REQ has APPLICATION tag 10 + var asReq = new KrbAsReq + { + Body = new KrbKdcReqBody + { + CName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { "test" } + }, + Realm = "TEST.COM", + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", "TEST.COM" } + }, + Till = DateTimeOffset.UtcNow.AddHours(1), + Nonce = 12345, + EType = new[] { EncryptionType.AES256_CTS_HMAC_SHA1_96 }, + } + }; + + var encoded = asReq.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kasn1 {base64}"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + // Should identify APPLICATION 10 as AS-REQ + Assert.IsTrue( + output.Contains("AS-REQ", StringComparison.OrdinalIgnoreCase) || + output.Contains("APPLICATION 10", StringComparison.OrdinalIgnoreCase) || + output.Contains("APPLICATION", StringComparison.OrdinalIgnoreCase), + $"Expected AS-REQ identification: {output}" + ); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosCredCommandTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosCredCommandTests.cs new file mode 100644 index 00000000..a23815fa --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosCredCommandTests.cs @@ -0,0 +1,239 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using Kerberos.NET.CommandLine; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosCredCommandTests : CommandLineTestBase + { + private static InputControl CreateIO(out StringWriter writer) + { + writer = new StringWriter(); + + return new InputControl + { + Clear = () => { }, + HookCtrlC = hook => { }, + ResetColor = () => { }, + SetColor = c => { }, + Writer = writer + }; + } + + private static KrbCred CreateTestKrbCred() + { + var encKey = new KrbEncryptionKey + { + EType = EncryptionType.AES256_CTS_HMAC_SHA1_96, + Usage = KeyUsage.EncTgsRepPartSessionKey, + KeyValue = new byte[32] + }; + + var ticket = new KrbTicket + { + Realm = "TEST.REALM", + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", "TEST.REALM" } + }, + EncryptedPart = new KrbEncryptedData + { + EType = EncryptionType.AES256_CTS_HMAC_SHA1_96, + Cipher = new byte[64] + } + }; + + var credInfo = new KrbCredInfo + { + Key = encKey, + Realm = "TEST.REALM", + PName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { "testuser" } + }, + SName = ticket.SName, + SRealm = "TEST.REALM", + Flags = TicketFlags.Forwardable | TicketFlags.Renewable, + AuthTime = DateTimeOffset.UtcNow.AddHours(-1), + EndTime = DateTimeOffset.UtcNow.AddHours(8), + StartTime = DateTimeOffset.UtcNow, + }; + + return KrbCred.WrapTicket(ticket, credInfo); + } + + [TestMethod] + public void CredCommandCanBeCreated() + { + var parameters = CommandLineParameters.Parse("kcred --decode foobar"); + var command = (KerberosCredCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("foobar", command.DecodeData); + } + + [TestMethod] + public void CredCommandAliasWorks() + { + var parameters = CommandLineParameters.Parse("cred --decode foobar"); + var command = parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsInstanceOfType(command, typeof(KerberosCredCommand)); + } + + [TestMethod] + public async Task CredCommandDecodesBase64KrbCred() + { + var krbCred = CreateTestKrbCred(); + var encoded = krbCred.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kcred --decode {base64}"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("KRB-CRED", StringComparison.OrdinalIgnoreCase), $"Expected KRB-CRED header: {output}"); + Assert.IsTrue(output.Contains("TEST.REALM", StringComparison.OrdinalIgnoreCase), $"Expected realm: {output}"); + Assert.IsTrue(output.Contains("Ticket", StringComparison.OrdinalIgnoreCase), $"Expected ticket info: {output}"); + } + + [TestMethod] + public async Task CredCommandDecodesFromFile() + { + var krbCred = CreateTestKrbCred(); + var encoded = krbCred.EncodeApplication(); + + using (var tmpFile = new TemporaryFile()) + { + File.WriteAllBytes(tmpFile.File, encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kcred --file \"{tmpFile.File}\""); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("KRB-CRED", StringComparison.OrdinalIgnoreCase), $"Expected KRB-CRED: {output}"); + Assert.IsTrue(output.Contains("TEST.REALM", StringComparison.OrdinalIgnoreCase), $"Expected realm: {output}"); + } + } + + [TestMethod] + public async Task CredCommandDecodesBase64FileContent() + { + var krbCred = CreateTestKrbCred(); + var encoded = krbCred.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + using (var tmpFile = new TemporaryFile()) + { + // Write base64 text to file (should auto-detect) + File.WriteAllText(tmpFile.File, base64); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kcred --file \"{tmpFile.File}\""); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("KRB-CRED", StringComparison.OrdinalIgnoreCase), $"Expected KRB-CRED: {output}"); + } + } + + [TestMethod] + public async Task CredCommandShowsCredentialInfo() + { + var krbCred = CreateTestKrbCred(); + var encoded = krbCred.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"kcred --decode {base64}"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + // Should show the unencrypted credential info since EType is NULL + Assert.IsTrue( + output.Contains("testuser", StringComparison.OrdinalIgnoreCase) || + output.Contains("Credential Info", StringComparison.OrdinalIgnoreCase), + $"Expected credential info details: {output}" + ); + } + + [TestMethod] + public async Task CredCommandHandlesInvalidBase64() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kcred --decode not-valid-base64!!!"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("not valid base64", StringComparison.OrdinalIgnoreCase) || + output.Contains("error", StringComparison.OrdinalIgnoreCase) || + output.Contains("Failed", StringComparison.OrdinalIgnoreCase), + $"Expected error message: {output}" + ); + } + + [TestMethod] + public async Task CredCommandHandlesMissingFile() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kcred --file nonexistent_file.kirbi"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("not found", StringComparison.OrdinalIgnoreCase) || + output.Contains("error", StringComparison.OrdinalIgnoreCase), + $"Expected file not found error: {output}" + ); + } + + [TestMethod] + public async Task CredCommandRequiresAction() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kcred"); + var command = parameters.CreateCommandExecutor(io); + + var result = await command.Execute(); + + var output = writer.ToString(); + + // Should prompt user that an action is needed + Assert.IsFalse(string.IsNullOrWhiteSpace(output)); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosFastCommandTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosFastCommandTests.cs new file mode 100644 index 00000000..fc8602f4 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosFastCommandTests.cs @@ -0,0 +1,153 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using Kerberos.NET.CommandLine; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosFastCommandTests : CommandLineTestBase + { + private static InputControl CreateIO(out StringWriter writer) + { + writer = new StringWriter(); + + return new InputControl + { + Clear = () => { }, + HookCtrlC = hook => { }, + ResetColor = () => { }, + SetColor = c => { }, + Writer = writer + }; + } + + [TestMethod] + public void FastCommandCanBeCreated() + { + var parameters = CommandLineParameters.Parse("kfast EXAMPLE.COM"); + var command = (KerberosFastCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("EXAMPLE.COM", command.Realm); + } + + [TestMethod] + public void FastCommandAliasWorks() + { + var parameters = CommandLineParameters.Parse("fast TEST.COM"); + var command = parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsInstanceOfType(command, typeof(KerberosFastCommand)); + } + + [TestMethod] + public void FastCommandParsesTestCf2Flag() + { + var parameters = CommandLineParameters.Parse("kfast --test-cf2"); + var command = (KerberosFastCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.TestCf2); + } + + [TestMethod] + public void FastCommandParsesProbeFlag() + { + var parameters = CommandLineParameters.Parse("kfast --probe EXAMPLE.COM"); + var command = (KerberosFastCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Probe); + } + + [TestMethod] + public void FastCommandParsesVerboseFlag() + { + var parameters = CommandLineParameters.Parse("kfast --verbose EXAMPLE.COM"); + var command = (KerberosFastCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Verbose); + } + + [TestMethod] + public async Task FastCommandCf2TestProducesOutput() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kfast --test-cf2"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("CF2", StringComparison.OrdinalIgnoreCase), + $"Expected CF2 test output: {output}" + ); + } + + [TestMethod] + public async Task FastCommandCf2TestShowsKeys() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kfast --test-cf2"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("Key1", StringComparison.OrdinalIgnoreCase) || + output.Contains("Key2", StringComparison.OrdinalIgnoreCase), + $"Expected key display: {output}" + ); + + Assert.IsTrue( + output.Contains("Result", StringComparison.OrdinalIgnoreCase) || + output.Contains("completed", StringComparison.OrdinalIgnoreCase), + $"Expected completion message: {output}" + ); + } + + [TestMethod] + public async Task FastCommandCf2TestShowsEncryptionType() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("kfast --test-cf2"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("AES128", StringComparison.OrdinalIgnoreCase), + $"Expected AES128 encryption type: {output}" + ); + } + + [TestMethod] + public async Task FastCommandCf2ProducesDeterministicResult() + { + var io1 = CreateIO(out var writer1); + var params1 = CommandLineParameters.Parse("kfast --test-cf2"); + var cmd1 = params1.CreateCommandExecutor(io1); + await cmd1.Execute(); + + var io2 = CreateIO(out var writer2); + var params2 = CommandLineParameters.Parse("kfast --test-cf2"); + var cmd2 = params2.CreateCommandExecutor(io2); + await cmd2.Execute(); + + // Same inputs should produce same CF2 result + Assert.AreEqual(writer1.ToString(), writer2.ToString()); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosKeytabMergeRemoveTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosKeytabMergeRemoveTests.cs new file mode 100644 index 00000000..c66bc7d1 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosKeytabMergeRemoveTests.cs @@ -0,0 +1,283 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Kerberos.NET.CommandLine; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosKeytabMergeRemoveTests : CommandLineTestBase + { + private static InputControl CreateIO(out StringWriter writer) + { + writer = new StringWriter(); + + return new InputControl + { + Clear = () => { }, + HookCtrlC = hook => { }, + ResetColor = () => { }, + SetColor = c => { }, + Writer = writer + }; + } + + private static string CreateKeytabFile(params (string principal, string realm, EncryptionType etype)[] entries) + { + var keytab = new KeyTable(); + + foreach (var entry in entries) + { + var key = new KerberosKey( + key: new byte[32], + etype: entry.etype, + principal: new PrincipalName(PrincipalNameType.NT_PRINCIPAL, entry.realm, new[] { entry.principal }) + ); + + keytab.Entries.Add(new KeyEntry(key)); + } + + var tmpFile = Path.GetTempFileName(); + + using (var fs = new FileStream(tmpFile, FileMode.Create)) + using (var writer = new BinaryWriter(fs)) + { + keytab.Write(writer); + } + + return tmpFile; + } + + [TestMethod] + public void KeytabCommandParsesMergeFlag() + { + var parameters = CommandLineParameters.Parse("kt source.keytab --merge other.keytab"); + var command = (KerberosKeytabCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("other.keytab", command.MergeFrom); + } + + [TestMethod] + public void KeytabCommandParsesRemoveFlag() + { + var parameters = CommandLineParameters.Parse("kt source.keytab --remove host/server.example.com"); + var command = (KerberosKeytabCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.AreEqual("host/server.example.com", command.RemovePrincipal); + } + + [TestMethod] + public void KeytabCommandRemoveETypeCanBeSet() + { + var parameters = CommandLineParameters.Parse("kt source.keytab --remove host/server"); + var command = (KerberosKeytabCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + // Nullable enum can't be parsed from command line, so set directly + command.RemoveEncryptionType = EncryptionType.AES256_CTS_HMAC_SHA1_96; + + Assert.AreEqual(EncryptionType.AES256_CTS_HMAC_SHA1_96, command.RemoveEncryptionType); + } + + [TestMethod] + public void KeytabCommandParsesOutputFlag() + { + var parameters = CommandLineParameters.Parse("kt source.keytab --merge other.keytab --output result.keytab"); + var command = (KerberosKeytabCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.AreEqual("result.keytab", command.OutputFile); + } + + [TestMethod] + public async Task KeytabCommandMergesKeytabs() + { + var sourceFile = CreateKeytabFile( + ("user1", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96) + ); + + var mergeFile = CreateKeytabFile( + ("user2", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96) + ); + + var outputFile = Path.GetTempFileName(); + + try + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse( + $"kt -f \"{sourceFile}\" --merge \"{mergeFile}\" --output \"{outputFile}\"" + ); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("Merged", StringComparison.OrdinalIgnoreCase), $"Expected merge message: {output}"); + + // Verify the output keytab has both entries + var result = new KeyTable(File.ReadAllBytes(outputFile)); + Assert.AreEqual(2, result.Entries.Count); + } + finally + { + File.Delete(sourceFile); + File.Delete(mergeFile); + File.Delete(outputFile); + } + } + + [TestMethod] + public async Task KeytabCommandMergeSkipsDuplicates() + { + var sourceFile = CreateKeytabFile( + ("user1", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96) + ); + + // Same principal as source + var mergeFile = CreateKeytabFile( + ("user1", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96) + ); + + var outputFile = Path.GetTempFileName(); + + try + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse( + $"kt -f \"{sourceFile}\" --merge \"{mergeFile}\" --output \"{outputFile}\"" + ); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("Merged 0", StringComparison.OrdinalIgnoreCase) || + output.Contains("Merged", StringComparison.OrdinalIgnoreCase), + $"Expected merge output: {output}"); + } + finally + { + File.Delete(sourceFile); + File.Delete(mergeFile); + File.Delete(outputFile); + } + } + + [TestMethod] + public async Task KeytabCommandRemovesEntries() + { + var sourceFile = CreateKeytabFile( + ("user1", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96), + ("user2", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96), + ("user3", "TEST.COM", EncryptionType.AES128_CTS_HMAC_SHA1_96) + ); + + var outputFile = Path.GetTempFileName(); + + try + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse( + $"kt -f \"{sourceFile}\" --remove user2 --output \"{outputFile}\"" + ); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("Removed", StringComparison.OrdinalIgnoreCase), $"Expected remove message: {output}"); + + // Verify user2 was removed + var result = new KeyTable(File.ReadAllBytes(outputFile)); + Assert.AreEqual(2, result.Entries.Count); + + Assert.IsFalse( + result.Entries.Any(e => e.Principal?.FullyQualifiedName?.Contains("user2") == true), + "user2 should have been removed" + ); + } + finally + { + File.Delete(sourceFile); + File.Delete(outputFile); + } + } + + [TestMethod] + public async Task KeytabCommandRemovesByEType() + { + var sourceFile = CreateKeytabFile( + ("user1", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96), + ("user1", "TEST.COM", EncryptionType.AES128_CTS_HMAC_SHA1_96) + ); + + var outputFile = Path.GetTempFileName(); + + try + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse( + $"kt -f \"{sourceFile}\" --remove user1 --output \"{outputFile}\"" + ); + var command = (KerberosKeytabCommand)parameters.CreateCommandExecutor(io); + command.RemoveEncryptionType = EncryptionType.AES128_CTS_HMAC_SHA1_96; + + await command.Execute(); + + // Should only remove the AES128 entry + var result = new KeyTable(File.ReadAllBytes(outputFile)); + Assert.AreEqual(1, result.Entries.Count); + Assert.AreEqual(EncryptionType.AES256_CTS_HMAC_SHA1_96, result.Entries.First().EncryptionType); + } + finally + { + File.Delete(sourceFile); + File.Delete(outputFile); + } + } + + [TestMethod] + public async Task KeytabCommandMergeHandlesMissingSource() + { + var sourceFile = CreateKeytabFile( + ("user1", "TEST.COM", EncryptionType.AES256_CTS_HMAC_SHA1_96) + ); + + try + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse( + $"kt -f \"{sourceFile}\" --merge nonexistent.keytab" + ); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("not found", StringComparison.OrdinalIgnoreCase) || + output.Contains("error", StringComparison.OrdinalIgnoreCase), + $"Expected error for missing merge source: {output}" + ); + } + finally + { + File.Delete(sourceFile); + } + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosProxyCommandTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosProxyCommandTests.cs new file mode 100644 index 00000000..c9359f40 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosProxyCommandTests.cs @@ -0,0 +1,130 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using Kerberos.NET.CommandLine; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosProxyCommandTests : CommandLineTestBase + { + private static InputControl CreateIO(out StringWriter writer) + { + writer = new StringWriter(); + + return new InputControl + { + Clear = () => { }, + HookCtrlC = hook => { }, + ResetColor = () => { }, + SetColor = c => { }, + Writer = writer + }; + } + + [TestMethod] + public void ProxyCommandCanBeCreated() + { + var parameters = CommandLineParameters.Parse("kproxy EXAMPLE.COM"); + var command = (KerberosProxyCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("EXAMPLE.COM", command.Realm); + } + + [TestMethod] + public void ProxyCommandAliasWorks() + { + var parameters = CommandLineParameters.Parse("proxy TEST.COM"); + var command = parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsInstanceOfType(command, typeof(KerberosProxyCommand)); + } + + [TestMethod] + public void ProxyCommandParsesUrl() + { + var parameters = CommandLineParameters.Parse("kproxy --url https://proxy.example.com/KdcProxy EXAMPLE.COM"); + var command = (KerberosProxyCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.AreEqual("https://proxy.example.com/KdcProxy", command.ProxyUrl); + Assert.AreEqual("EXAMPLE.COM", command.Realm); + } + + [TestMethod] + public void ProxyCommandParsesVerbose() + { + var parameters = CommandLineParameters.Parse("kproxy --verbose EXAMPLE.COM"); + var command = (KerberosProxyCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Verbose); + } + + [TestMethod] + public async Task ProxyCommandOutputsHeader() + { + var io = CreateIO(out var writer); + + // Use a non-existent URL so it fails fast but we can verify output format + var parameters = CommandLineParameters.Parse("kproxy --url https://127.0.0.1:1/KdcProxy TEST.REALM"); + var command = (KerberosProxyCommand)parameters.CreateCommandExecutor(io); + command.Timeout = TimeSpan.FromSeconds(1); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("KDC Proxy", StringComparison.OrdinalIgnoreCase) || + output.Contains("Connectivity", StringComparison.OrdinalIgnoreCase), + $"Expected connectivity test header: {output}" + ); + } + + [TestMethod] + public async Task ProxyCommandHandlesConnectionFailure() + { + var io = CreateIO(out var writer); + + var parameters = CommandLineParameters.Parse("kproxy --url https://127.0.0.1:1/KdcProxy NONEXISTENT.REALM"); + var command = (KerberosProxyCommand)parameters.CreateCommandExecutor(io); + command.Timeout = TimeSpan.FromSeconds(1); + + await command.Execute(); + + var output = writer.ToString(); + + // Should report the error gracefully + Assert.IsTrue( + output.Contains("Error", StringComparison.OrdinalIgnoreCase) || + output.Contains("timed out", StringComparison.OrdinalIgnoreCase) || + output.Contains("failed", StringComparison.OrdinalIgnoreCase) || + output.Contains("Time Elapsed", StringComparison.OrdinalIgnoreCase), + $"Expected error report in output: {output}" + ); + } + + [TestMethod] + public async Task ProxyCommandDisplaysRealm() + { + var io = CreateIO(out var writer); + + var parameters = CommandLineParameters.Parse("kproxy --url https://127.0.0.1:1/KdcProxy MY.TEST.REALM"); + var command = (KerberosProxyCommand)parameters.CreateCommandExecutor(io); + command.Timeout = TimeSpan.FromSeconds(1); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue(output.Contains("MY.TEST.REALM"), $"Expected realm in output: {output}"); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosTokenDecodeCommandTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosTokenDecodeCommandTests.cs new file mode 100644 index 00000000..dfa73677 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosTokenDecodeCommandTests.cs @@ -0,0 +1,176 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.IO; +using System.Threading.Tasks; +using Kerberos.NET.CommandLine; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosTokenDecodeCommandTests : CommandLineTestBase + { + private static InputControl CreateIO(out StringWriter writer) + { + writer = new StringWriter(); + + return new InputControl + { + Clear = () => { }, + HookCtrlC = hook => { }, + ResetColor = () => { }, + SetColor = c => { }, + Writer = writer + }; + } + + [TestMethod] + public void TokenCommandCanBeCreated() + { + var parameters = CommandLineParameters.Parse("ktoken foobar"); + var command = (KerberosTokenDecodeCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("foobar", command.Token); + } + + [TestMethod] + public void TokenCommandAliasWorks() + { + var parameters = CommandLineParameters.Parse("token foobar"); + var command = parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsInstanceOfType(command, typeof(KerberosTokenDecodeCommand)); + } + + [TestMethod] + public async Task TokenCommandRequiresInput() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("ktoken"); + var command = parameters.CreateCommandExecutor(io); + + var result = await command.Execute(); + + Assert.IsFalse(result); + } + + [TestMethod] + public async Task TokenCommandDecodesNegotiateHeader() + { + // BaseTest.RC4Header starts with "Negotiate " prefix; extract just the base64 part + var token = BaseTest.RC4Header.Replace("Negotiate ", "").Trim(); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("ktoken placeholder"); + var command = (KerberosTokenDecodeCommand)parameters.CreateCommandExecutor(io); + command.Token = token; + + await command.Execute(); + + var output = writer.ToString(); + + // Should detect it's a SPNEGO/negotiate token + Assert.IsTrue( + output.Contains("SPNEGO", StringComparison.OrdinalIgnoreCase) || + output.Contains("Negotiate", StringComparison.OrdinalIgnoreCase) || + output.Contains("AP-REQ", StringComparison.OrdinalIgnoreCase) || + output.Contains("Mechanism", StringComparison.OrdinalIgnoreCase), + $"Expected token type identification in output: {output}" + ); + } + + [TestMethod] + public async Task TokenCommandStripsNegotiatePrefix() + { + // RC4Header already has "Negotiate " prefix - pass it directly to the command + // to verify the prefix-stripping logic works + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("ktoken placeholder"); + var command = (KerberosTokenDecodeCommand)parameters.CreateCommandExecutor(io); + command.Token = BaseTest.RC4Header; + + await command.Execute(); + + var output = writer.ToString(); + + // Should still work after stripping the Negotiate prefix + Assert.IsFalse(string.IsNullOrWhiteSpace(output)); + Assert.IsFalse(output.Contains("not valid base64", StringComparison.OrdinalIgnoreCase)); + } + + [TestMethod] + public async Task TokenCommandHandlesInvalidBase64() + { + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse("ktoken not-valid-base64!!!"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsFalse(string.IsNullOrWhiteSpace(output)); + } + + [TestMethod] + public async Task TokenCommandVerboseFlag() + { + var parameters = CommandLineParameters.Parse("ktoken --verbose foobar"); + var command = (KerberosTokenDecodeCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Verbose); + } + + [TestMethod] + public async Task TokenCommandRawFlag() + { + var parameters = CommandLineParameters.Parse("ktoken --raw foobar"); + var command = (KerberosTokenDecodeCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Raw); + } + + [TestMethod] + public async Task TokenCommandDecodesKrbError() + { + // Create a minimal KRB-ERROR and encode it + var error = new KrbError + { + ErrorCode = KerberosErrorCode.KRB_AP_ERR_TKT_EXPIRED, + Realm = "TEST.REALM", + EText = "Ticket expired", + STime = DateTimeOffset.UtcNow, + SName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_SRV_INST, + Name = new[] { "krbtgt", "TEST.REALM" } + } + }; + + var encoded = error.EncodeApplication(); + var base64 = Convert.ToBase64String(encoded.ToArray()); + + var io = CreateIO(out var writer); + var parameters = CommandLineParameters.Parse($"ktoken {base64}"); + var command = parameters.CreateCommandExecutor(io); + + await command.Execute(); + + var output = writer.ToString(); + + Assert.IsTrue( + output.Contains("KRB-ERROR", StringComparison.OrdinalIgnoreCase) || + output.Contains("Error", StringComparison.OrdinalIgnoreCase) || + output.Contains("TEST.REALM", StringComparison.OrdinalIgnoreCase), + $"Expected KRB-ERROR details in output: {output}" + ); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/KerberosWhoAmIExtendedTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/KerberosWhoAmIExtendedTests.cs new file mode 100644 index 00000000..11c507c2 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/KerberosWhoAmIExtendedTests.cs @@ -0,0 +1,91 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System.IO; +using Kerberos.NET.CommandLine; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KerberosWhoAmIExtendedTests : CommandLineTestBase + { + [TestMethod] + public void WhoAmIParsesSignaturesFlag() + { + var parameters = CommandLineParameters.Parse("whoami --signatures"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsTrue(command.Signatures); + } + + [TestMethod] + public void WhoAmIParsesDelegationFlag() + { + var parameters = CommandLineParameters.Parse("whoami --delegation"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsTrue(command.Delegation); + } + + [TestMethod] + public void WhoAmIParsesAllFlag() + { + var parameters = CommandLineParameters.Parse("whoami --all"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.IsTrue(command.All); + } + + [TestMethod] + public void WhoAmIParsesCombinedFlags() + { + var parameters = CommandLineParameters.Parse("whoami --logon --groups --claims --signatures --delegation"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Logon); + Assert.IsTrue(command.Groups); + Assert.IsTrue(command.Claims); + Assert.IsTrue(command.Signatures); + Assert.IsTrue(command.Delegation); + Assert.IsFalse(command.All); + } + + [TestMethod] + public void WhoAmIParsesCache() + { + var parameters = CommandLineParameters.Parse("whoami --cache /tmp/krb5cc_test"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.AreEqual("/tmp/krb5cc_test", command.Cache); + } + + [TestMethod] + public void WhoAmIParsesVerbose() + { + var parameters = CommandLineParameters.Parse("whoami --verbose"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsTrue(command.Verbose); + } + + [TestMethod] + public void WhoAmIDefaultFlagsAreOff() + { + var parameters = CommandLineParameters.Parse("whoami"); + var command = (KerberosWhoAmI)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsFalse(command.All); + Assert.IsFalse(command.Logon); + Assert.IsFalse(command.Groups); + Assert.IsFalse(command.Claims); + Assert.IsFalse(command.Signatures); + Assert.IsFalse(command.Delegation); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/CommandLine/TransportSelectionTests.cs b/Tests/Tests.Kerberos.NET/CommandLine/TransportSelectionTests.cs new file mode 100644 index 00000000..27b0ff2b --- /dev/null +++ b/Tests/Tests.Kerberos.NET/CommandLine/TransportSelectionTests.cs @@ -0,0 +1,188 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System.IO; +using System.Linq; +using Kerberos.NET.Client; +using Kerberos.NET.CommandLine; +using Kerberos.NET.Transport; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class TransportSelectionTests : CommandLineTestBase + { + [TestMethod] + public void KinitParsesTransportFlag() + { + var parameters = CommandLineParameters.Parse("kinit --transport tcp user@EXAMPLE.COM"); + var command = (KerberosInitCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("tcp", command.Transport); + } + + [TestMethod] + public void KlistParsesTransportFlag() + { + var parameters = CommandLineParameters.Parse("klist --transport https"); + var command = (KerberosListCommand)parameters.CreateCommandExecutor(InputControl.Default()); + + Assert.IsNotNull(command); + Assert.AreEqual("https", command.Transport); + } + + [TestMethod] + public void ConfigureTransportSelectsTcp() + { + using (var client = new KerberosClient()) + { + KerberosInitCommand.ConfigureTransport(client, "tcp"); + + foreach (var t in client.Transports) + { + if (t is TcpKerberosTransport) + { + Assert.IsTrue(t.Enabled, "TCP should be enabled"); + } + else + { + Assert.IsFalse(t.Enabled, $"{t.GetType().Name} should be disabled"); + } + } + } + } + + [TestMethod] + public void ConfigureTransportSelectsUdp() + { + using (var client = new KerberosClient()) + { + KerberosInitCommand.ConfigureTransport(client, "udp"); + + foreach (var t in client.Transports) + { + if (t is UdpKerberosTransport) + { + Assert.IsTrue(t.Enabled, "UDP should be enabled"); + } + else + { + Assert.IsFalse(t.Enabled, $"{t.GetType().Name} should be disabled"); + } + } + } + } + + [TestMethod] + public void ConfigureTransportSelectsHttps() + { + using (var client = new KerberosClient()) + { + KerberosInitCommand.ConfigureTransport(client, "https"); + + foreach (var t in client.Transports) + { + if (t is HttpsKerberosTransport) + { + Assert.IsTrue(t.Enabled, "HTTPS should be enabled"); + } + else + { + Assert.IsFalse(t.Enabled, $"{t.GetType().Name} should be disabled"); + } + } + } + } + + [TestMethod] + public void ConfigureTransportProxyAliasSelectsHttps() + { + using (var client = new KerberosClient()) + { + KerberosInitCommand.ConfigureTransport(client, "proxy"); + + foreach (var t in client.Transports) + { + if (t is HttpsKerberosTransport) + { + Assert.IsTrue(t.Enabled, "HTTPS should be enabled for 'proxy' alias"); + } + else + { + Assert.IsFalse(t.Enabled, $"{t.GetType().Name} should be disabled"); + } + } + } + } + + [TestMethod] + public void ConfigureTransportNullDoesNothing() + { + using (var client = new KerberosClient()) + { + var beforeStates = client.Transports.Select(t => t.Enabled).ToArray(); + + KerberosInitCommand.ConfigureTransport(client, null); + + var afterStates = client.Transports.Select(t => t.Enabled).ToArray(); + + CollectionAssert.AreEqual(beforeStates, afterStates); + } + } + + [TestMethod] + public void ConfigureTransportEmptyDoesNothing() + { + using (var client = new KerberosClient()) + { + var beforeStates = client.Transports.Select(t => t.Enabled).ToArray(); + + KerberosInitCommand.ConfigureTransport(client, ""); + + var afterStates = client.Transports.Select(t => t.Enabled).ToArray(); + + CollectionAssert.AreEqual(beforeStates, afterStates); + } + } + + [TestMethod] + public void ConfigureTransportUnknownDoesNothing() + { + using (var client = new KerberosClient()) + { + var beforeStates = client.Transports.Select(t => t.Enabled).ToArray(); + + KerberosInitCommand.ConfigureTransport(client, "unknown"); + + var afterStates = client.Transports.Select(t => t.Enabled).ToArray(); + + CollectionAssert.AreEqual(beforeStates, afterStates); + } + } + + [TestMethod] + public void ConfigureTransportIsCaseInsensitive() + { + using (var client = new KerberosClient()) + { + KerberosInitCommand.ConfigureTransport(client, "TCP"); + + foreach (var t in client.Transports) + { + if (t is TcpKerberosTransport) + { + Assert.IsTrue(t.Enabled, "TCP should be enabled with uppercase input"); + } + else + { + Assert.IsFalse(t.Enabled, $"{t.GetType().Name} should be disabled"); + } + } + } + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Entities/AnonymousPkinitTests.cs b/Tests/Tests.Kerberos.NET/Entities/AnonymousPkinitTests.cs new file mode 100644 index 00000000..21332eca --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Entities/AnonymousPkinitTests.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Linq; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class AnonymousPkinitTests : BaseTest + { + [TestMethod] + public void Anonymous_WellKnownPrincipal_HasCorrectType() + { + var anonymous = KrbPrincipalName.WellKnown.Anonymous(); + + Assert.AreEqual(PrincipalNameType.NT_WELLKNOWN, anonymous.Type); + } + + [TestMethod] + public void Anonymous_WellKnownPrincipal_HasCorrectName() + { + var anonymous = KrbPrincipalName.WellKnown.Anonymous(); + + Assert.AreEqual(2, anonymous.Name.Length); + Assert.AreEqual("WELLKNOWN", anonymous.Name[0]); + Assert.AreEqual("ANONYMOUS", anonymous.Name[1]); + } + + [TestMethod] + public void Anonymous_Realm_HasCorrectValue() + { + // KerberosConstants is internal, so we verify the well-known + // anonymous realm value is used correctly through the public API. + // The constant value per RFC 8062 is "WELLKNOWN:ANONYMOUS". + var anonymous = KrbPrincipalName.WellKnown.Anonymous(); + + // Verify that the anonymous principal can be created and has the + // expected well-known name, which is paired with realm "WELLKNOWN:ANONYMOUS" + Assert.AreEqual(PrincipalNameType.NT_WELLKNOWN, anonymous.Type); + Assert.AreEqual("WELLKNOWN", anonymous.Name[0]); + } + + [TestMethod] + public void Anonymous_NtAnonymousType_HasCorrectValue() + { + Assert.AreEqual(14, (int)PrincipalNameType.NT_ANONYMOUS); + } + + [TestMethod] + public void Anonymous_PrincipalName_FullyQualifiedName() + { + var anonymous = KrbPrincipalName.WellKnown.Anonymous(); + + var fqn = anonymous.FullyQualifiedName; + + Assert.IsNotNull(fqn); + Assert.IsTrue(fqn.Contains("WELLKNOWN")); + Assert.IsTrue(fqn.Contains("ANONYMOUS")); + } + + [TestMethod] + public void Anonymous_PrincipalName_Encode_Decode_RoundTrip() + { + var original = KrbPrincipalName.WellKnown.Anonymous(); + + var encoded = original.Encode(); + + Assert.IsTrue(encoded.Length > 0); + + var decoded = KrbPrincipalName.Decode(encoded); + + Assert.AreEqual(original.Type, decoded.Type); + Assert.AreEqual(original.Name.Length, decoded.Name.Length); + Assert.AreEqual(original.Name[0], decoded.Name[0]); + Assert.AreEqual(original.Name[1], decoded.Name[1]); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Entities/CammacTests.cs b/Tests/Tests.Kerberos.NET/Entities/CammacTests.cs new file mode 100644 index 00000000..b56249a7 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Entities/CammacTests.cs @@ -0,0 +1,182 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class CammacTests : BaseTest + { + private static KrbAuthorizationData[] CreateTestElements() + { + return new[] + { + new KrbAuthorizationData + { + Type = AuthorizationDataType.AdIfRelevant, + Data = new byte[] { 1, 2, 3 } + } + }; + } + + private static KerberosKey GenerateKey() + { + var keyData = KrbEncryptionKey.Generate(EncryptionType.AES256_CTS_HMAC_SHA1_96); + return keyData.AsKey(); + } + + [TestMethod] + public void KrbAdCammac_Create_WithKdcKeyOnly() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey); + + Assert.IsNotNull(cammac.Elements); + Assert.AreEqual(1, cammac.Elements.Length); + Assert.IsNotNull(cammac.KdcVerifier); + Assert.IsNull(cammac.ServiceVerifier); + } + + [TestMethod] + public void KrbAdCammac_Create_WithBothKeys() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + var serviceKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey, serviceKey); + + Assert.IsNotNull(cammac.Elements); + Assert.AreEqual(1, cammac.Elements.Length); + Assert.IsNotNull(cammac.KdcVerifier); + Assert.IsNotNull(cammac.ServiceVerifier); + } + + [TestMethod] + public void KrbAdCammac_ValidateKdcVerifier_Success() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey); + + Assert.IsTrue(cammac.ValidateKdcVerifier(kdcKey)); + } + + [TestMethod] + public void KrbAdCammac_ValidateKdcVerifier_WrongKey_Fails() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + var wrongKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey); + + Assert.IsFalse(cammac.ValidateKdcVerifier(wrongKey)); + } + + [TestMethod] + public void KrbAdCammac_ValidateServiceVerifier_Success() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + var serviceKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey, serviceKey); + + Assert.IsTrue(cammac.ValidateServiceVerifier(serviceKey)); + } + + [TestMethod] + public void KrbAdCammac_ValidateServiceVerifier_NullVerifier_ReturnsFalse() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey); + + Assert.IsFalse(cammac.ValidateServiceVerifier(GenerateKey())); + } + + [TestMethod] + public void KrbAdCammac_ToAuthorizationData() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey); + var authData = cammac.ToAuthorizationData(); + + Assert.AreEqual(AuthorizationDataType.AdCammac, authData.Type); + Assert.IsTrue(authData.Data.Length > 0); + } + + [TestMethod] + public void KrbAdCammac_Encode_Decode_RoundTrip() + { + var elements = CreateTestElements(); + var kdcKey = GenerateKey(); + var serviceKey = GenerateKey(); + + var cammac = KrbAdCammac.Create(elements, kdcKey, serviceKey); + + var encoded = cammac.Encode(); + var decoded = KrbAdCammac.Decode(encoded); + + Assert.IsNotNull(decoded.Elements); + Assert.AreEqual(cammac.Elements.Length, decoded.Elements.Length); + Assert.IsNotNull(decoded.KdcVerifier); + Assert.IsNotNull(decoded.ServiceVerifier); + + Assert.IsTrue(decoded.ValidateKdcVerifier(kdcKey)); + Assert.IsTrue(decoded.ValidateServiceVerifier(serviceKey)); + } + + [TestMethod] + public void KrbVerifierMac_Encode_Decode_RoundTrip() + { + var kdcKey = GenerateKey(); + var elements = CreateTestElements(); + + var cammac = KrbAdCammac.Create(elements, kdcKey); + var verifier = cammac.KdcVerifier; + + var encoded = verifier.Encode(); + var decoded = KrbVerifierMac.Decode(encoded); + + Assert.AreEqual(verifier.EncryptionType, decoded.EncryptionType); + Assert.IsNotNull(decoded.Mac); + Assert.AreEqual(verifier.Mac.Type, decoded.Mac.Type); + } + + [TestMethod] + public void KrbAdCammac_Create_NullElements_Throws() + { + var kdcKey = GenerateKey(); + + Assert.ThrowsException(() => + { + KrbAdCammac.Create(null, kdcKey); + }); + } + + [TestMethod] + public void KrbAdCammac_Create_NullKdcKey_Throws() + { + var elements = CreateTestElements(); + + Assert.ThrowsException(() => + { + KrbAdCammac.Create(elements, null); + }); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Entities/KrbSafePrivTests.cs b/Tests/Tests.Kerberos.NET/Entities/KrbSafePrivTests.cs new file mode 100644 index 00000000..a213ab3e --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Entities/KrbSafePrivTests.cs @@ -0,0 +1,221 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Text; +using Kerberos.NET.Client; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class KrbSafePrivTests : BaseTest + { + private static KerberosKey GenerateKey() + { + return KrbEncryptionKey.Generate(EncryptionType.AES256_CTS_HMAC_SHA1_96).AsKey(); + } + + private static KrbHostAddress CreateLocalhostAddress() + { + return new KrbHostAddress + { + AddressType = AddressType.IPv4, + Address = new byte[] { 127, 0, 0, 1 } + }; + } + + [TestMethod] + public void KrbSafe_Create_Verify_RoundTrip() + { + var key = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("hello kerberos"); + var sender = CreateLocalhostAddress(); + + var safe = KrbSafe.Create(userData, key, sender); + + var result = safe.Verify(key); + + CollectionAssert.AreEqual(userData, result.ToArray()); + } + + [TestMethod] + [ExpectedException(typeof(Exception), AllowDerivedTypes = true)] + public void KrbSafe_Verify_WrongKey_Throws() + { + var key = GenerateKey(); + var wrongKey = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("secret data"); + var sender = CreateLocalhostAddress(); + + var safe = KrbSafe.Create(userData, key, sender); + + safe.Verify(wrongKey); + } + + [TestMethod] + public void KrbSafe_Create_SetsProtocolFields() + { + var key = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("test"); + var sender = CreateLocalhostAddress(); + + var safe = KrbSafe.Create(userData, key, sender); + + Assert.AreEqual(5, safe.ProtocolVersionNumber); + Assert.AreEqual(MessageType.KRB_SAFE, safe.MessageType); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void KrbSafe_Create_NullKey_Throws() + { + var userData = Encoding.UTF8.GetBytes("test"); + var sender = CreateLocalhostAddress(); + + KrbSafe.Create(userData, null, sender); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void KrbSafe_Verify_NullKey_Throws() + { + var key = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("test"); + var sender = CreateLocalhostAddress(); + + var safe = KrbSafe.Create(userData, key, sender); + + safe.Verify(null); + } + + [TestMethod] + public void KrbPriv_Create_Decrypt_RoundTrip() + { + var key = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("confidential payload"); + var sender = CreateLocalhostAddress(); + + var privPart = new KrbEncKrbPrivPart + { + UserData = userData, + Timestamp = DateTimeOffset.UtcNow, + Usec = 0, + SeqNumber = 1, + SAddress = sender + }; + + var priv = KrbPriv.Create(key, privPart); + + var decrypted = priv.Decrypt(key); + + CollectionAssert.AreEqual(userData, decrypted.UserData.ToArray()); + } + + [TestMethod] + [ExpectedException(typeof(Exception), AllowDerivedTypes = true)] + public void KrbPriv_Decrypt_WrongKey_Throws() + { + var key = GenerateKey(); + var wrongKey = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("confidential"); + var sender = CreateLocalhostAddress(); + + var privPart = new KrbEncKrbPrivPart + { + UserData = userData, + Timestamp = DateTimeOffset.UtcNow, + Usec = 0, + SeqNumber = 1, + SAddress = sender + }; + + var priv = KrbPriv.Create(key, privPart); + + priv.Decrypt(wrongKey); + } + + [TestMethod] + public void KrbPriv_Create_SetsProtocolFields() + { + var key = GenerateKey(); + var userData = Encoding.UTF8.GetBytes("test"); + var sender = CreateLocalhostAddress(); + + var privPart = new KrbEncKrbPrivPart + { + UserData = userData, + Timestamp = DateTimeOffset.UtcNow, + Usec = 0, + SeqNumber = 1, + SAddress = sender + }; + + var priv = KrbPriv.Create(key, privPart); + + Assert.AreEqual(5, priv.ProtocolVersionNumber); + Assert.AreEqual(MessageType.KRB_PRIV, priv.MessageType); + } + + [TestMethod] + public void KerberosMessageService_MakeSafe_VerifySafe_RoundTrip() + { + var key = GenerateKey(); + var localAddr = CreateLocalhostAddress(); + var remoteAddr = new KrbHostAddress + { + AddressType = AddressType.IPv4, + Address = new byte[] { 10, 0, 0, 1 } + }; + + var service = new KerberosMessageService(key, localAddr, remoteAddr); + + var userData = Encoding.UTF8.GetBytes("safe message"); + + var safe = service.MakeSafe(userData); + var result = service.VerifySafe(safe); + + CollectionAssert.AreEqual(userData, result.ToArray()); + } + + [TestMethod] + public void KerberosMessageService_MakePriv_DecryptPriv_RoundTrip() + { + var key = GenerateKey(); + var localAddr = CreateLocalhostAddress(); + var remoteAddr = new KrbHostAddress + { + AddressType = AddressType.IPv4, + Address = new byte[] { 10, 0, 0, 1 } + }; + + var service = new KerberosMessageService(key, localAddr, remoteAddr); + + var userData = Encoding.UTF8.GetBytes("private message"); + + var priv = service.MakePriv(userData); + var result = service.DecryptPriv(priv); + + CollectionAssert.AreEqual(userData, result.ToArray()); + } + + [TestMethod] + public void KerberosMessageService_SequenceNumberIncrements() + { + var key = GenerateKey(); + var localAddr = CreateLocalhostAddress(); + + var service = new KerberosMessageService(key, localAddr, initialSequenceNumber: 10); + + var safe1 = service.MakeSafe(Encoding.UTF8.GetBytes("msg1")); + var safe2 = service.MakeSafe(Encoding.UTF8.GetBytes("msg2")); + + Assert.AreEqual(10, safe1.SafeBody.SeqNumber); + Assert.AreEqual(11, safe2.SafeBody.SeqNumber); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Kdc/FreshnessTests.cs b/Tests/Tests.Kerberos.NET/Kdc/FreshnessTests.cs new file mode 100644 index 00000000..d58c618f --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Kdc/FreshnessTests.cs @@ -0,0 +1,176 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Server; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class FreshnessTests : BaseTest + { + private const string Realm = "CORP.IDENTITYINTERVENTION.COM"; + + private static FakeRealmService CreateRealmService() + { + return new FakeRealmService(Realm); + } + + private static IKerberosPrincipal FindKrbtgt(FakeRealmService realmService) + { + return realmService.Principals.Find( + KrbPrincipalName.WellKnown.Krbtgt(Realm), + Realm + ); + } + + [TestMethod] + public void FreshnessHandler_PostValidate_AddsFreshnessToken() + { + var realmService = CreateRealmService(); + var handler = new PaDataFreshnessHandler(realmService); + + var principal = FindKrbtgt(realmService); + var preAuthList = new List(); + + handler.PostValidate(principal, preAuthList); + + Assert.IsTrue(preAuthList.Count > 0, "PostValidate should add a PA-Data entry"); + Assert.IsTrue( + preAuthList.Any(p => p.Type == PaDataType.PA_AS_FRESHNESS), + "PostValidate should add a PA_AS_FRESHNESS entry" + ); + } + + [TestMethod] + public void FreshnessHandler_PostValidate_NullPrincipal_Throws() + { + var realmService = CreateRealmService(); + var handler = new PaDataFreshnessHandler(realmService); + + var preAuthList = new List(); + + Assert.ThrowsException(() => + { + handler.PostValidate(null, preAuthList); + }); + } + + [TestMethod] + public void FreshnessHandler_PostValidate_NullList_Throws() + { + var realmService = CreateRealmService(); + var handler = new PaDataFreshnessHandler(realmService); + + var principal = FindKrbtgt(realmService); + + Assert.ThrowsException(() => + { + handler.PostValidate(principal, null); + }); + } + + [TestMethod] + public void ValidateFreshnessToken_ValidToken_ReturnsTrue() + { + var realmService = CreateRealmService(); + var handler = new PaDataFreshnessHandler(realmService); + + var principal = FindKrbtgt(realmService); + var preAuthList = new List(); + + handler.PostValidate(principal, preAuthList); + + var freshnessToken = preAuthList.First(p => p.Type == PaDataType.PA_AS_FRESHNESS); + + var krbtgtPrincipal = FindKrbtgt(realmService); + var kdcKey = krbtgtPrincipal.RetrieveLongTermCredential(); + + var isValid = PaDataFreshnessHandler.ValidateFreshnessToken( + freshnessToken.Value, + kdcKey, + TimeSpan.FromMinutes(5), + DateTimeOffset.UtcNow + ); + + Assert.IsTrue(isValid, "A freshly generated token should be valid"); + } + + [TestMethod] + public void ValidateFreshnessToken_ExpiredToken_ReturnsFalse() + { + var realmService = CreateRealmService(); + var handler = new PaDataFreshnessHandler(realmService); + + var principal = FindKrbtgt(realmService); + var preAuthList = new List(); + + handler.PostValidate(principal, preAuthList); + + var freshnessToken = preAuthList.First(p => p.Type == PaDataType.PA_AS_FRESHNESS); + + var krbtgtPrincipal = FindKrbtgt(realmService); + var kdcKey = krbtgtPrincipal.RetrieveLongTermCredential(); + + var isValid = PaDataFreshnessHandler.ValidateFreshnessToken( + freshnessToken.Value, + kdcKey, + TimeSpan.FromMinutes(5), + DateTimeOffset.UtcNow.AddHours(24) + ); + + Assert.IsFalse(isValid, "A token validated far in the future should be expired"); + } + + [TestMethod] + public void ValidateFreshnessToken_WrongKey_ReturnsFalse() + { + var realmService = CreateRealmService(); + var handler = new PaDataFreshnessHandler(realmService); + + var principal = FindKrbtgt(realmService); + var preAuthList = new List(); + + handler.PostValidate(principal, preAuthList); + + var freshnessToken = preAuthList.First(p => p.Type == PaDataType.PA_AS_FRESHNESS); + + var wrongKeyData = KrbEncryptionKey.Generate(EncryptionType.AES256_CTS_HMAC_SHA1_96); + var wrongKey = wrongKeyData.AsKey(); + + var isValid = PaDataFreshnessHandler.ValidateFreshnessToken( + freshnessToken.Value, + wrongKey, + TimeSpan.FromMinutes(5), + DateTimeOffset.UtcNow + ); + + Assert.IsFalse(isValid, "Validation with a wrong key should fail"); + } + + [TestMethod] + public void ValidateFreshnessToken_GarbageData_ReturnsFalse() + { + var wrongKeyData = KrbEncryptionKey.Generate(EncryptionType.AES256_CTS_HMAC_SHA1_96); + var wrongKey = wrongKeyData.AsKey(); + + var garbageData = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04 }; + + var isValid = PaDataFreshnessHandler.ValidateFreshnessToken( + garbageData, + wrongKey, + TimeSpan.FromMinutes(5), + DateTimeOffset.UtcNow + ); + + Assert.IsFalse(isValid, "Garbage data should not validate"); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Kdc/RbcdTests.cs b/Tests/Tests.Kerberos.NET/Kdc/RbcdTests.cs new file mode 100644 index 00000000..e56bf104 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Kdc/RbcdTests.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using Kerberos.NET.Entities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class RbcdTests : BaseTest + { + [TestMethod] + public void PacOptions_ResourceBasedConstrainedDelegation_FlagExists() + { + var flag = PacOptions.ResourceBasedConstrainedDelegation; + + Assert.AreEqual(1 << 28, (int)flag); + Assert.IsTrue(Enum.IsDefined(typeof(PacOptions), flag)); + } + + [TestMethod] + public void KdcOptions_ConstrainedDelegation_FlagExists() + { + var flag = KdcOptions.ConstrainedDelegation; + + Assert.AreEqual(1 << 17, (int)flag); + Assert.IsTrue(Enum.IsDefined(typeof(KdcOptions), flag)); + } + + [TestMethod] + public void PacOptions_Encode_Decode_RoundTrip() + { + var original = new KrbPaPacOptions + { + Flags = PacOptions.ResourceBasedConstrainedDelegation | PacOptions.Claims | PacOptions.BranchAware + }; + + var encoded = original.Encode(); + + Assert.IsTrue(encoded.Length > 0); + + var decoded = KrbPaPacOptions.Decode(encoded); + + Assert.AreEqual(original.Flags, decoded.Flags); + Assert.IsTrue(decoded.Flags.HasFlag(PacOptions.ResourceBasedConstrainedDelegation)); + Assert.IsTrue(decoded.Flags.HasFlag(PacOptions.Claims)); + Assert.IsTrue(decoded.Flags.HasFlag(PacOptions.BranchAware)); + } + + [TestMethod] + public void KdcOptions_ConstrainedDelegation_CombinedWithOtherFlags() + { + var combined = KdcOptions.ConstrainedDelegation | KdcOptions.Canonicalize | KdcOptions.Renewable; + + Assert.IsTrue(combined.HasFlag(KdcOptions.ConstrainedDelegation)); + Assert.IsTrue(combined.HasFlag(KdcOptions.Canonicalize)); + Assert.IsTrue(combined.HasFlag(KdcOptions.Renewable)); + Assert.IsFalse(combined.HasFlag(KdcOptions.Forwardable)); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Kdc/SpakeTests.cs b/Tests/Tests.Kerberos.NET/Kdc/SpakeTests.cs new file mode 100644 index 00000000..c47337f6 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Kdc/SpakeTests.cs @@ -0,0 +1,222 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Security.Cryptography; +using Kerberos.NET.Configuration; +using Kerberos.NET.Crypto; +using Kerberos.NET.Entities; +using Kerberos.NET.Server; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class SpakeTests : BaseTest + { + [TestMethod] + public void SpakeExchange_GetKeySize_AllGroups() + { + Assert.AreEqual(32, SpakeExchange.GetKeySize(SpakePreAuthGroupType.Edwards25519)); + Assert.AreEqual(32, SpakeExchange.GetKeySize(SpakePreAuthGroupType.P_256)); + Assert.AreEqual(48, SpakeExchange.GetKeySize(SpakePreAuthGroupType.P_384)); + Assert.AreEqual(66, SpakeExchange.GetKeySize(SpakePreAuthGroupType.P_521)); + } + + [TestMethod] + public void SpakeExchange_GetHashAlgorithm_AllGroups() + { + Assert.AreEqual(HashAlgorithmName.SHA256, SpakeExchange.GetHashAlgorithm(SpakePreAuthGroupType.Edwards25519)); + Assert.AreEqual(HashAlgorithmName.SHA256, SpakeExchange.GetHashAlgorithm(SpakePreAuthGroupType.P_256)); + Assert.AreEqual(HashAlgorithmName.SHA384, SpakeExchange.GetHashAlgorithm(SpakePreAuthGroupType.P_384)); + Assert.AreEqual(HashAlgorithmName.SHA512, SpakeExchange.GetHashAlgorithm(SpakePreAuthGroupType.P_521)); + } + + [TestMethod] + public void SpakeExchange_GenerateScalar_CorrectLength() + { + var groups = new[] + { + SpakePreAuthGroupType.Edwards25519, + SpakePreAuthGroupType.P_256, + SpakePreAuthGroupType.P_384, + SpakePreAuthGroupType.P_521 + }; + + foreach (var group in groups) + { + var scalar = SpakeExchange.GenerateScalar(group); + var expectedSize = SpakeExchange.GetKeySize(group); + + Assert.AreEqual(expectedSize, scalar.Length, $"Scalar length mismatch for group {group}"); + } + } + + [TestMethod] + public void SpakeExchange_GenerateScalar_IsRandom() + { + var groups = new[] + { + SpakePreAuthGroupType.Edwards25519, + SpakePreAuthGroupType.P_256, + SpakePreAuthGroupType.P_384, + SpakePreAuthGroupType.P_521 + }; + + foreach (var group in groups) + { + var scalar1 = SpakeExchange.GenerateScalar(group); + var scalar2 = SpakeExchange.GenerateScalar(group); + + Assert.IsFalse( + scalar1.SequenceEqual(scalar2), + $"Two generated scalars should differ for group {group}"); + } + } + + [TestMethod] + public void SpakeExchange_DeriveKey_Deterministic() + { + var group = SpakePreAuthGroupType.P_256; + var sharedSecret = new byte[32]; + var transcript = new byte[64]; + + Array.Fill(sharedSecret, 0xAA); + Array.Fill(transcript, 0xBB); + + var key1 = SpakeExchange.DeriveKey(group, sharedSecret, transcript); + var key2 = SpakeExchange.DeriveKey(group, sharedSecret, transcript); + + Assert.IsNotNull(key1); + Assert.IsTrue(key1.Length > 0); + Assert.IsTrue(key1.SequenceEqual(key2), "Same inputs must produce the same derived key"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void SpakeExchange_DeriveKey_NullSharedSecret_Throws() + { + SpakeExchange.DeriveKey(SpakePreAuthGroupType.P_256, null, new byte[64]); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void SpakeExchange_DeriveKey_NullTranscript_Throws() + { + SpakeExchange.DeriveKey(SpakePreAuthGroupType.P_256, new byte[32], null); + } + + [TestMethod] + public void SpakeExchange_DeriveKey_DifferentInputs_DifferentOutput() + { + var group = SpakePreAuthGroupType.P_256; + var transcript = new byte[64]; + Array.Fill(transcript, 0xCC); + + var secret1 = new byte[32]; + var secret2 = new byte[32]; + Array.Fill(secret1, 0x11); + Array.Fill(secret2, 0x22); + + var key1 = SpakeExchange.DeriveKey(group, secret1, transcript); + var key2 = SpakeExchange.DeriveKey(group, secret2, transcript); + + Assert.IsFalse( + key1.SequenceEqual(key2), + "Different shared secrets must produce different derived keys"); + } + + [TestMethod] + public void SpakeState_DefaultValues() + { + var state = new SpakeState(); + + Assert.AreEqual(default(SpakePreAuthGroupType), state.SelectedGroup); + Assert.IsFalse(state.ChallengeSent); + Assert.IsNull(state.ServerPrivateKey); + Assert.IsNull(state.SharedSecret); + } + + [TestMethod] + public void PaDataSpakeHandler_PreValidate_SkipsWhenNotSupported() + { + var realmService = new FakeRealmService("CORP.TEST.COM"); + var handler = new PaDataSpakeHandler(realmService); + + var principal = new FakeKerberosPrincipal("user@CORP.TEST.COM") + { + SupportedPreAuthenticationTypes = new[] { PaDataType.PA_ENC_TIMESTAMP } + }; + + var preauth = new PreAuthenticationContext + { + Principal = principal + }; + + handler.PreValidate(preauth); + + Assert.IsFalse( + preauth.PreAuthenticationState.ContainsKey(PaDataType.PA_SPAKE), + "SPAKE state should not be created when principal does not support PA_SPAKE"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void PaDataSpakeHandler_PreValidate_NullPreauth_Throws() + { + var realmService = new FakeRealmService("CORP.TEST.COM"); + var handler = new PaDataSpakeHandler(realmService); + + handler.PreValidate(null); + } + + [TestMethod] + public void PaDataSpakeHandler_Validate_NoSpakeData_ReturnsChallenge() + { + var realmService = new FakeRealmService("CORP.TEST.COM"); + var handler = new PaDataSpakeHandler(realmService); + + var principal = new FakeKerberosPrincipal("user@CORP.TEST.COM") + { + SupportedPreAuthenticationTypes = new[] + { + PaDataType.PA_ENC_TIMESTAMP, + PaDataType.PA_SPAKE + } + }; + + var preauth = new PreAuthenticationContext + { + Principal = principal + }; + + // Pre-validate to initialize SPAKE state + handler.PreValidate(preauth); + + // Create an AS-REQ without any PA_SPAKE data + var asReq = new KrbAsReq + { + Body = new KrbKdcReqBody + { + CName = new KrbPrincipalName + { + Type = PrincipalNameType.NT_PRINCIPAL, + Name = new[] { "user@CORP.TEST.COM" } + }, + Realm = "CORP.TEST.COM", + EType = new[] { EncryptionType.AES256_CTS_HMAC_SHA1_96 } + }, + PaData = Array.Empty() + }; + + var result = handler.Validate(asReq, preauth); + + Assert.IsNotNull(result, "First round should return a challenge PA-Data"); + Assert.AreEqual(PaDataType.PA_SPAKE, result.Type, "Challenge PA-Data should have PA_SPAKE type"); + Assert.IsTrue(result.Value.Length > 0, "Challenge PA-Data should contain data"); + } + } +} diff --git a/Tests/Tests.Kerberos.NET/Pac/PacExtensionTests.cs b/Tests/Tests.Kerberos.NET/Pac/PacExtensionTests.cs new file mode 100644 index 00000000..605fab09 --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Pac/PacExtensionTests.cs @@ -0,0 +1,136 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using Kerberos.NET.Entities; +using Kerberos.NET.Entities.Pac; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class PacExtensionTests : BaseTest + { + [TestMethod] + public void PacAttributesInfo_RoundTrip_PacWasRequested() + { + var original = new PacAttributesInfo + { + FlagsLength = 32, + Flags = PacAttributeFlags.PacWasRequested + }; + + var marshalled = original.Marshal(); + + var restored = new PacAttributesInfo(); + restored.Unmarshal(marshalled); + + Assert.AreEqual(original.FlagsLength, restored.FlagsLength); + Assert.AreEqual(original.Flags, restored.Flags); + } + + [TestMethod] + public void PacAttributesInfo_PacType_IsAttributesInfo() + { + var info = new PacAttributesInfo(); + + Assert.AreEqual(PacType.ATTRIBUTES_INFO, info.PacType); + } + + [TestMethod] + public void PacAttributesInfo_PacWasGivenImplicitly_RoundTrips() + { + var original = new PacAttributesInfo + { + FlagsLength = 32, + Flags = PacAttributeFlags.PacWasGivenImplicitly + }; + + var marshalled = original.Marshal(); + + var restored = new PacAttributesInfo(); + restored.Unmarshal(marshalled); + + Assert.AreEqual(PacAttributeFlags.PacWasGivenImplicitly, restored.Flags); + } + + [TestMethod] + public void PacAttributesInfo_DefaultFlagsLength_Is32() + { + var info = new PacAttributesInfo(); + + Assert.AreEqual(32, info.FlagsLength); + } + + [TestMethod] + public void PacRequestor_RoundTrip_Sid() + { + var sid = new SecurityIdentifier( + IdentifierAuthority.NTAuthority, + new uint[] { 123, 456, 789 }, + SidAttributes.SE_GROUP_ENABLED + ); + + var original = new PacRequestor + { + RequestorSid = sid + }; + + var marshalled = original.Marshal(); + + var restored = new PacRequestor(); + restored.Unmarshal(marshalled); + + Assert.AreEqual(sid.Value, restored.RequestorSid.Value); + } + + [TestMethod] + public void PacRequestor_PacType_IsRequestor() + { + var requestor = new PacRequestor(); + + Assert.AreEqual(PacType.REQUESTOR, requestor.PacType); + } + + [TestMethod] + public void PacType_TicketChecksum_Is0x10() + { + Assert.AreEqual(0x10, (int)PacType.TICKET_CHECKSUM); + } + + [TestMethod] + public void PacType_AttributesInfo_Is0x11() + { + Assert.AreEqual(0x11, (int)PacType.ATTRIBUTES_INFO); + } + + [TestMethod] + public void PacType_Requestor_Is0x12() + { + Assert.AreEqual(0x12, (int)PacType.REQUESTOR); + } + + [TestMethod] + public void PacType_FullChecksum_Is0x13() + { + Assert.AreEqual(0x13, (int)PacType.FULL_CHECKSUM); + } + + [TestMethod] + public void KnownTypes_ContainsAllNewPacTypes() + { + var knownTypesField = typeof(PrivilegedAttributeCertificate) + .GetField("KnownTypes", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + + Assert.IsNotNull(knownTypesField, "KnownTypes field not found"); + + var knownTypes = (System.Collections.Generic.Dictionary)knownTypesField.GetValue(null); + + Assert.IsTrue(knownTypes.ContainsKey(PacType.TICKET_CHECKSUM), "KnownTypes missing TICKET_CHECKSUM"); + Assert.IsTrue(knownTypes.ContainsKey(PacType.ATTRIBUTES_INFO), "KnownTypes missing ATTRIBUTES_INFO"); + Assert.IsTrue(knownTypes.ContainsKey(PacType.REQUESTOR), "KnownTypes missing REQUESTOR"); + Assert.IsTrue(knownTypes.ContainsKey(PacType.FULL_CHECKSUM), "KnownTypes missing FULL_CHECKSUM"); + } + } +} From e273b3c54bdd7a0311fb173110932eef23dc0fa1 Mon Sep 17 00:00:00 2001 From: Steve Syfuhs Date: Thu, 26 Mar 2026 22:26:11 -0700 Subject: [PATCH 4/4] DH Linux and ECDH everywhere --- .../KerberosAsymmetricCredential.cs | 53 +- .../Crypto/Pal/Linux/LinuxCryptoPal.cs | 30 +- .../Crypto/Pal/Windows/WindowsCryptoPal.cs | 6 +- Kerberos.NET/Crypto/Pkinit/EcdhKey.cs | 75 +++ .../Crypto/Pkinit/EcdhKeyAgreement.cs | 230 ++++++++ .../Crypto/Pkinit/ManagedDiffieHellman.cs | 223 ++++++++ .../ManagedDiffieHellmanOakleyGroup14.cs | 24 + .../ManagedDiffieHellmanOakleyGroup2.cs | 24 + Kerberos.NET/Server/PaDataPkAsReqHandler.cs | 34 +- .../Crypto/CryptoPalTests.cs | 21 +- .../Crypto/DiffieHellmanKeyAgreementTests.cs | 18 +- .../Crypto/ManagedDiffieHellman.cs | 193 ------- .../Crypto/ManagedDiffieHellmanOakley14.cs | 15 - .../Crypto/ManagedDiffieHellmanOakley2.cs | 15 - .../Crypto/PkinitCrossPlatformTests.cs | 526 ++++++++++++++++++ .../End2End/ClientToKdcE2ETests.cs | 12 +- 16 files changed, 1238 insertions(+), 261 deletions(-) create mode 100644 Kerberos.NET/Crypto/Pkinit/EcdhKey.cs create mode 100644 Kerberos.NET/Crypto/Pkinit/EcdhKeyAgreement.cs create mode 100644 Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellman.cs create mode 100644 Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup14.cs create mode 100644 Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup2.cs delete mode 100644 Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellman.cs delete mode 100644 Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley14.cs delete mode 100644 Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley2.cs create mode 100644 Tests/Tests.Kerberos.NET/Crypto/PkinitCrossPlatformTests.cs diff --git a/Kerberos.NET/Credentials/KerberosAsymmetricCredential.cs b/Kerberos.NET/Credentials/KerberosAsymmetricCredential.cs index 0abf94ba..a1b433f9 100644 --- a/Kerberos.NET/Credentials/KerberosAsymmetricCredential.cs +++ b/Kerberos.NET/Credentials/KerberosAsymmetricCredential.cs @@ -9,6 +9,7 @@ using System.Security.Cryptography.Asn1; using System.Security.Cryptography.Pkcs; using System.Security.Cryptography.X509Certificates; +using Kerberos.NET.Asn1; using Kerberos.NET.Crypto; using Kerberos.NET.Entities; using static Kerberos.NET.Entities.KerberosConstants; @@ -23,6 +24,7 @@ public class KerberosAsymmetricCredential : KerberosCredential, IDisposable { private static readonly Oid IdPkInitAuthData = new Oid("1.3.6.1.5.2.3.1"); private static readonly Oid DiffieHellman = new Oid("1.2.840.10046.2.1"); + private static readonly Oid EllipticCurveDiffieHellman = new Oid("1.2.840.10045.2.1"); private ReadOnlyMemory clientDHNonce; private IKeyAgreement agreement; @@ -257,9 +259,43 @@ public override void TransformKdcReq(KrbKdcReq req) private static Exception OnlyKeyAgreementSupportedException() => throw new NotSupportedException("Only key agreement is supported for PKINIT authentication"); - private KrbAuthPack CreateEllipticCurveDiffieHellmanAuthPack(KrbKdcReqBody _) + private KrbAuthPack CreateEllipticCurveDiffieHellmanAuthPack(KrbKdcReqBody body) { - throw new NotImplementedException(); + using (var sha1 = CryptoPal.Platform.Sha1()) + { + var encoded = body.Encode(); + var paChecksum = sha1.ComputeHash(encoded.Span); + + // Encode the curve OID as the algorithm parameters + var curveOid = EcdhKeyAgreement.GetCurveOid(this.KeyAgreement); + + ReadOnlyMemory curveOidEncoded; + using (var writer = new AsnWriter(AsnEncodingRules.DER)) + { + writer.WriteObjectIdentifier(curveOid.Value); + curveOidEncoded = writer.EncodeAsMemory(); + } + + var authPack = new KrbAuthPack + { + PKAuthenticator = new KrbPKAuthenticator + { + Nonce = body.Nonce, + PaChecksum = paChecksum + }, + ClientPublicValue = new KrbSubjectPublicKeyInfo + { + Algorithm = new KrbAlgorithmIdentifier + { + Algorithm = EllipticCurveDiffieHellman, + Parameters = curveOidEncoded + }, + SubjectPublicKey = this.agreement.PublicKey.EncodePublicKey() + } + }; + + return authPack; + } } private KrbAuthPack CreateDiffieHellmanAuthPack(KrbKdcReqBody body) @@ -351,7 +387,18 @@ private ReadOnlyMemory DeriveDHKeyAgreement(KrbKdcRep kdcRep, KrbPaPkAsRep { var dhKeyInfo = this.ValidateDHReply(pkRep); - var kdcPublicKey = DiffieHellmanKey.ParsePublicKey(dhKeyInfo.SubjectPublicKey, this.agreement.PublicKey.KeyLength); + IExchangeKey kdcPublicKey; + + if (this.SupportsEllipticCurveDiffieHellman && this.agreement.PublicKey is EcdhKey) + { + // ECDH: SubjectPublicKey is an uncompressed EC point (04 || x || y) + kdcPublicKey = EcdhKey.ParsePublicKey(dhKeyInfo.SubjectPublicKey, this.agreement.PublicKey.Algorithm); + } + else + { + // DH: SubjectPublicKey is an ASN.1 DER-encoded INTEGER + kdcPublicKey = DiffieHellmanKey.ParsePublicKey(dhKeyInfo.SubjectPublicKey, this.agreement.PublicKey.KeyLength); + } this.agreement.ImportPartnerKey(kdcPublicKey); diff --git a/Kerberos.NET/Crypto/Pal/Linux/LinuxCryptoPal.cs b/Kerberos.NET/Crypto/Pal/Linux/LinuxCryptoPal.cs index 3148465f..fed1630f 100644 --- a/Kerberos.NET/Crypto/Pal/Linux/LinuxCryptoPal.cs +++ b/Kerberos.NET/Crypto/Pal/Linux/LinuxCryptoPal.cs @@ -42,18 +42,34 @@ protected override void PlatformCheck() public override ISymmetricAlgorithm Aes() => new AesAlgorithm(); - public override IKeyAgreement DiffieHellmanP256() => throw PlatformNotSupported("ECDH-P256"); + public override IKeyAgreement DiffieHellmanP256() => new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256); - public override IKeyAgreement DiffieHellmanP384() => throw PlatformNotSupported("ECDH-P384"); + public override IKeyAgreement DiffieHellmanP384() => new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384); - public override IKeyAgreement DiffieHellmanP521() => throw PlatformNotSupported("ECDH-P521"); + public override IKeyAgreement DiffieHellmanP521() => new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521); - public override IKeyAgreement DiffieHellmanModp2() => throw PlatformNotSupported("DH-MODP-2"); + public override IKeyAgreement DiffieHellmanModp2() => new ManagedDiffieHellmanOakleyGroup2(); - public override IKeyAgreement DiffieHellmanModp2(IExchangeKey privateKey) => throw PlatformNotSupported("DH-MODP-2"); + public override IKeyAgreement DiffieHellmanModp2(IExchangeKey privateKey) + { + if (privateKey is DiffieHellmanKey dhKey) + { + return new ManagedDiffieHellmanOakleyGroup2(dhKey); + } + + return this.DiffieHellmanModp2(); + } - public override IKeyAgreement DiffieHellmanModp14() => throw PlatformNotSupported("DH-MODP-14"); + public override IKeyAgreement DiffieHellmanModp14() => new ManagedDiffieHellmanOakleyGroup14(); - public override IKeyAgreement DiffieHellmanModp14(IExchangeKey privateKey) => throw PlatformNotSupported("DH-MODP-14"); + public override IKeyAgreement DiffieHellmanModp14(IExchangeKey privateKey) + { + if (privateKey is DiffieHellmanKey dhKey) + { + return new ManagedDiffieHellmanOakleyGroup14(dhKey); + } + + return this.DiffieHellmanModp14(); + } } } diff --git a/Kerberos.NET/Crypto/Pal/Windows/WindowsCryptoPal.cs b/Kerberos.NET/Crypto/Pal/Windows/WindowsCryptoPal.cs index 2fa7822b..8f5bbc8a 100644 --- a/Kerberos.NET/Crypto/Pal/Windows/WindowsCryptoPal.cs +++ b/Kerberos.NET/Crypto/Pal/Windows/WindowsCryptoPal.cs @@ -41,11 +41,11 @@ protected override void PlatformCheck() public override ISymmetricAlgorithm Aes() => new AesAlgorithm(); - public override IKeyAgreement DiffieHellmanP256() => throw PlatformNotSupported("ECDH-P256"); + public override IKeyAgreement DiffieHellmanP256() => new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256); - public override IKeyAgreement DiffieHellmanP384() => throw PlatformNotSupported("ECDH-P384"); + public override IKeyAgreement DiffieHellmanP384() => new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384); - public override IKeyAgreement DiffieHellmanP521() => throw PlatformNotSupported("ECDH-P521"); + public override IKeyAgreement DiffieHellmanP521() => new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521); public override IKeyAgreement DiffieHellmanModp2() => new BCryptDiffieHellmanOakleyGroup2(); diff --git a/Kerberos.NET/Crypto/Pkinit/EcdhKey.cs b/Kerberos.NET/Crypto/Pkinit/EcdhKey.cs new file mode 100644 index 00000000..e97eb06a --- /dev/null +++ b/Kerberos.NET/Crypto/Pkinit/EcdhKey.cs @@ -0,0 +1,75 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; + +namespace Kerberos.NET.Crypto +{ + /// + /// Represents an elliptic curve Diffie-Hellman key for PKINIT. + /// The public component is the uncompressed EC point (04 || x || y). + /// + public class EcdhKey : IExchangeKey + { + public AsymmetricKeyType Type { get; set; } + + public KeyAgreementAlgorithm Algorithm { get; set; } + + public DateTimeOffset? CacheExpiry { get; set; } + + public int KeyLength { get; set; } + + public ReadOnlyMemory PublicComponent { get; set; } + + public ReadOnlyMemory PrivateComponent { get; set; } + + /// + /// For ECDH in PKINIT, the SubjectPublicKey is the raw uncompressed EC point + /// encoded as a BIT STRING value. Per RFC 5480, the public key is just the + /// uncompressed point (04 || x || y). + /// + public ReadOnlyMemory EncodePublicKey() + { + return this.PublicComponent; + } + + /// + /// Parse an EC public key from the SubjectPublicKey BIT STRING value. + /// The value is an uncompressed point (04 || x || y). + /// + public static EcdhKey ParsePublicKey(ReadOnlyMemory data, KeyAgreementAlgorithm algorithm) + { + var keyData = data; + + // The SubjectPublicKey may have the uncompressed point directly + // or may be wrapped. We expect the uncompressed format: 04 || x || y + if (keyData.Length > 0 && keyData.Span[0] == 0x04) + { + // Already in uncompressed format + } + else if (keyData.Length > 1 && keyData.Span[0] == 0x00) + { + // BIT STRING padding byte + keyData = keyData.Slice(1); + } + + int coordinateSize = algorithm switch + { + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256 => 32, + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384 => 48, + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521 => 66, + _ => throw new ArgumentException($"Unsupported EC algorithm: {algorithm}") + }; + + return new EcdhKey + { + Algorithm = algorithm, + Type = AsymmetricKeyType.Public, + KeyLength = coordinateSize, + PublicComponent = keyData.ToArray() + }; + } + } +} diff --git a/Kerberos.NET/Crypto/Pkinit/EcdhKeyAgreement.cs b/Kerberos.NET/Crypto/Pkinit/EcdhKeyAgreement.cs new file mode 100644 index 00000000..fdbefe77 --- /dev/null +++ b/Kerberos.NET/Crypto/Pkinit/EcdhKeyAgreement.cs @@ -0,0 +1,230 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Reflection; +using System.Security.Cryptography; + +namespace Kerberos.NET.Crypto +{ + /// + /// Cross-platform elliptic curve Diffie-Hellman key agreement for PKINIT. + /// Supports NIST P-256, P-384, and P-521 curves per RFC 4556. + /// Uses runtime reflection to access ECDiffieHellman which is available on + /// .NET Core 3.1+ / .NET 5+ even when compiled against netstandard2.0. + /// + public class EcdhKeyAgreement : IKeyAgreement + { + private readonly IDisposable ecdh; + private readonly KeyAgreementAlgorithm algorithm; + private readonly int coordinateSize; + private IDisposable partnerEcdhForKey; + private bool disposedValue; + + // Cached reflection info + private static readonly Type EcdhType; + private static readonly MethodInfo CreateWithCurveMethod; + private static readonly MethodInfo ExportParametersMethod; + private static readonly MethodInfo ImportParametersMethod; + private static readonly MethodInfo DeriveKeyFromHashMethod; + private static readonly PropertyInfo PublicKeyProperty; + + static EcdhKeyAgreement() + { + EcdhType = Type.GetType("System.Security.Cryptography.ECDiffieHellman, System.Security.Cryptography.Algorithms") + ?? Type.GetType("System.Security.Cryptography.ECDiffieHellman, System.Core") + ?? Type.GetType("System.Security.Cryptography.ECDiffieHellman, System.Security.Cryptography"); + + if (EcdhType != null) + { + CreateWithCurveMethod = EcdhType.GetMethod("Create", new[] { typeof(ECCurve) }); + ExportParametersMethod = EcdhType.GetMethod("ExportParameters", new[] { typeof(bool) }); + ImportParametersMethod = EcdhType.GetMethod("ImportParameters", new[] { typeof(ECParameters) }); + DeriveKeyFromHashMethod = EcdhType.GetMethod("DeriveKeyFromHash", new[] { + Type.GetType("System.Security.Cryptography.ECDiffieHellmanPublicKey, System.Security.Cryptography.Algorithms") + ?? Type.GetType("System.Security.Cryptography.ECDiffieHellmanPublicKey, System.Core") + ?? Type.GetType("System.Security.Cryptography.ECDiffieHellmanPublicKey, System.Security.Cryptography"), + typeof(HashAlgorithmName) + }); + PublicKeyProperty = EcdhType.GetProperty("PublicKey"); + } + } + + public EcdhKeyAgreement(KeyAgreementAlgorithm algorithm) + { + if (EcdhType == null || CreateWithCurveMethod == null) + { + throw new PlatformNotSupportedException( + "ECDH PKINIT requires a runtime that supports ECDiffieHellman " + + "(e.g., .NET Core 3.1+, .NET 5+). Alternatively, use DiffieHellmanModp14." + ); + } + + this.algorithm = algorithm; + + var curve = algorithm switch + { + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256 => ECCurve.NamedCurves.nistP256, + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384 => ECCurve.NamedCurves.nistP384, + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521 => ECCurve.NamedCurves.nistP521, + _ => throw new ArgumentException($"Unsupported EC algorithm: {algorithm}", nameof(algorithm)) + }; + + this.ecdh = (IDisposable)CreateWithCurveMethod.Invoke(null, new object[] { curve }); + + var ecParams = (ECParameters)ExportParametersMethod.Invoke(this.ecdh, new object[] { false }); + this.coordinateSize = ecParams.Q.X.Length; + + // Build uncompressed EC point: 04 || x || y + var publicPoint = new byte[1 + ecParams.Q.X.Length + ecParams.Q.Y.Length]; + publicPoint[0] = 0x04; + Buffer.BlockCopy(ecParams.Q.X, 0, publicPoint, 1, ecParams.Q.X.Length); + Buffer.BlockCopy(ecParams.Q.Y, 0, publicPoint, 1 + ecParams.Q.X.Length, ecParams.Q.Y.Length); + + this.PublicKey = new EcdhKey + { + Type = AsymmetricKeyType.Public, + Algorithm = algorithm, + KeyLength = this.coordinateSize, + PublicComponent = publicPoint + }; + + this.PrivateKey = new EcdhKey + { + Type = AsymmetricKeyType.Private, + Algorithm = algorithm, + KeyLength = this.coordinateSize, + PublicComponent = publicPoint + }; + } + + public IExchangeKey PublicKey { get; } + + public IExchangeKey PrivateKey { get; } + + public ReadOnlyMemory GenerateAgreement() + { + if (this.partnerEcdhForKey == null) + { + throw new InvalidOperationException("A partner key must be imported first"); + } + + // Use DeriveKeyFromHash to get the shared secret + var partnerPubKey = PublicKeyProperty.GetValue(this.partnerEcdhForKey); + + var derived = (byte[])DeriveKeyFromHashMethod.Invoke( + this.ecdh, + new object[] { partnerPubKey, HashAlgorithmName.SHA256 } + ); + + // For PKINIT, we need a deterministic shared secret of coordinate size. + // DeriveKeyFromHash(SHA-256) gives 32 bytes which matches P-256. + // For P-384 and P-521, we'll use what we get and String2Key will handle sizing. + if (derived.Length > this.coordinateSize) + { + var truncated = new byte[this.coordinateSize]; + Buffer.BlockCopy(derived, 0, truncated, 0, this.coordinateSize); + return truncated; + } + + return derived; + } + + public void ImportPartnerKey(IExchangeKey publicKey) + { + if (publicKey == null) + { + throw new ArgumentNullException(nameof(publicKey)); + } + + var keyData = publicKey.PublicComponent.ToArray(); + + // Parse the uncompressed EC point: 04 || x || y + if (keyData.Length == 0 || keyData[0] != 0x04) + { + throw new CryptographicException("Only uncompressed EC point format (0x04) is supported"); + } + + var coordSize = (keyData.Length - 1) / 2; + var x = new byte[coordSize]; + var y = new byte[coordSize]; + + Buffer.BlockCopy(keyData, 1, x, 0, coordSize); + Buffer.BlockCopy(keyData, 1 + coordSize, y, 0, coordSize); + + var curve = this.algorithm switch + { + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256 => ECCurve.NamedCurves.nistP256, + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384 => ECCurve.NamedCurves.nistP384, + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521 => ECCurve.NamedCurves.nistP521, + _ => throw new CryptographicException($"Unsupported algorithm: {this.algorithm}") + }; + + var ecParams = new ECParameters + { + Curve = curve, + Q = new ECPoint { X = x, Y = y } + }; + + // Create a new ECDiffieHellman from partner parameters to get their public key + this.partnerEcdhForKey?.Dispose(); + this.partnerEcdhForKey = (IDisposable)CreateWithCurveMethod.Invoke(null, new object[] { curve }); + ImportParametersMethod.Invoke(this.partnerEcdhForKey, new object[] { ecParams }); + } + + /// + /// Maps a to its corresponding EC curve OID. + /// + public static Oid GetCurveOid(KeyAgreementAlgorithm algorithm) + { + return algorithm switch + { + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256 => new Oid("1.2.840.10045.3.1.7"), // secp256r1 / P-256 + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384 => new Oid("1.3.132.0.34"), // secp384r1 / P-384 + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521 => new Oid("1.3.132.0.35"), // secp521r1 / P-521 + _ => throw new ArgumentException($"Not an EC algorithm: {algorithm}", nameof(algorithm)) + }; + } + + /// + /// Maps a curve OID to the corresponding . + /// + public static KeyAgreementAlgorithm FromCurveOid(string oidValue) + { + return oidValue switch + { + "1.2.840.10045.3.1.7" => KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256, + "1.3.132.0.34" => KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384, + "1.3.132.0.35" => KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521, + _ => throw new CryptographicException($"Unsupported EC curve OID: {oidValue}") + }; + } + + /// + /// Returns true if ECDH key agreement is available on the current runtime. + /// + public static bool IsSupported => EcdhType != null && CreateWithCurveMethod != null; + + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + this.ecdh?.Dispose(); + this.partnerEcdhForKey?.Dispose(); + } + + this.disposedValue = true; + } + } + + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellman.cs b/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellman.cs new file mode 100644 index 00000000..0d71e0e5 --- /dev/null +++ b/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellman.cs @@ -0,0 +1,223 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Numerics; +using System.Security.Cryptography; + +namespace Kerberos.NET.Crypto +{ + /// + /// Cross-platform managed Diffie-Hellman key agreement implementation using BigInteger. + /// Used on Linux/macOS where platform-native DH (BCrypt) is not available. + /// + public abstract class ManagedDiffieHellman : IKeyAgreement + { + private readonly int keyLength; + + private readonly BigInteger prime; + private readonly BigInteger generator; + private readonly BigInteger factor; + private readonly BigInteger privateKey; + private readonly BigInteger publicValue; + + private BigInteger partnerKey; + private bool disposedValue; + + protected ManagedDiffieHellman(ReadOnlyMemory prime, ReadOnlyMemory generator, ReadOnlyMemory factor) + { + this.keyLength = prime.Length; + + this.prime = ToBigInteger(prime); + this.generator = ToBigInteger(generator); + this.factor = ToBigInteger(factor); + + this.privateKey = GeneratePrivateKey(this.keyLength); + this.publicValue = BigInteger.ModPow(this.generator, this.privateKey, this.prime); + + this.PublicKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Public, + Generator = ToFixedBytes(this.generator, this.keyLength), + Modulus = ToFixedBytes(this.prime, this.keyLength), + PublicComponent = ToFixedBytes(this.publicValue, this.keyLength), + Factor = ToFixedBytes(this.factor, this.keyLength), + KeyLength = this.keyLength + }; + + this.PrivateKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Private, + Generator = ToFixedBytes(this.generator, this.keyLength), + Modulus = ToFixedBytes(this.prime, this.keyLength), + PublicComponent = ToFixedBytes(this.publicValue, this.keyLength), + Factor = ToFixedBytes(this.factor, this.keyLength), + PrivateComponent = ToFixedBytes(this.privateKey, this.keyLength), + KeyLength = this.keyLength + }; + } + + protected ManagedDiffieHellman(DiffieHellmanKey importKey) + { + if (importKey == null) + { + throw new ArgumentNullException(nameof(importKey)); + } + + this.keyLength = importKey.KeyLength; + + this.prime = ToBigInteger(importKey.Modulus); + this.generator = ToBigInteger(importKey.Generator); + this.factor = ToBigInteger(importKey.Factor); + this.privateKey = ToBigInteger(importKey.PrivateComponent); + this.publicValue = ToBigInteger(importKey.PublicComponent); + + this.PublicKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Public, + Generator = importKey.Generator, + Modulus = importKey.Modulus, + PublicComponent = importKey.PublicComponent, + Factor = importKey.Factor, + KeyLength = this.keyLength, + CacheExpiry = importKey.CacheExpiry + }; + + this.PrivateKey = new DiffieHellmanKey + { + Type = AsymmetricKeyType.Private, + Generator = importKey.Generator, + Modulus = importKey.Modulus, + PublicComponent = importKey.PublicComponent, + Factor = importKey.Factor, + PrivateComponent = importKey.PrivateComponent, + KeyLength = this.keyLength, + CacheExpiry = importKey.CacheExpiry + }; + } + + private static BigInteger GeneratePrivateKey(int keyLength) + { + // Generate a random private key x where 2 <= x < p-1 + // We generate keyLength random bytes and ensure it's positive and in range + + var bytes = new byte[keyLength]; + + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(bytes); + } + + // Ensure positive by appending a zero byte (BigInteger is little-endian) + var withSign = new byte[keyLength + 1]; + Array.Reverse(bytes); // Convert from big-endian to little-endian for BigInteger + Array.Copy(bytes, 0, withSign, 0, keyLength); + withSign[keyLength] = 0; // ensure positive + + var result = new BigInteger(withSign); + + // Ensure it's at least 2 + if (result < 2) + { + result = 2; + } + + return result; + } + + /// + /// Parse big-endian bytes to a positive BigInteger. + /// + private static BigInteger ToBigInteger(ReadOnlyMemory bigEndianBytes) + { + var arr = bigEndianBytes.ToArray(); + Array.Reverse(arr); // big-endian to little-endian + + // Ensure positive by adding a zero byte if the high bit is set + if (arr.Length > 0 && arr[arr.Length - 1] >= 0x80) + { + var padded = new byte[arr.Length + 1]; + Array.Copy(arr, 0, padded, 0, arr.Length); + return new BigInteger(padded); + } + + return new BigInteger(arr); + } + + /// + /// Convert a BigInteger to a fixed-length big-endian byte array. + /// + private static byte[] ToFixedBytes(BigInteger value, int length) + { + var littleEndian = value.ToByteArray(); + Array.Reverse(littleEndian); // to big-endian + + // Strip leading zeros added by BigInteger for sign + int start = 0; + while (start < littleEndian.Length - 1 && littleEndian[start] == 0) + { + start++; + } + + var trimmed = new byte[littleEndian.Length - start]; + Array.Copy(littleEndian, start, trimmed, 0, trimmed.Length); + + if (trimmed.Length >= length) + { + // Take the last 'length' bytes + var result = new byte[length]; + Array.Copy(trimmed, trimmed.Length - length, result, 0, length); + return result; + } + else + { + // Pad with leading zeros + var result = new byte[length]; + Array.Copy(trimmed, 0, result, length - trimmed.Length, trimmed.Length); + return result; + } + } + + public IExchangeKey PublicKey { get; } + + public IExchangeKey PrivateKey { get; } + + public ReadOnlyMemory GenerateAgreement() + { + if (this.partnerKey == BigInteger.Zero) + { + throw new InvalidOperationException("A partner key must be imported first"); + } + + var sharedSecret = BigInteger.ModPow(this.partnerKey, this.privateKey, this.prime); + + return ToFixedBytes(sharedSecret, this.keyLength); + } + + public void ImportPartnerKey(IExchangeKey publicKey) + { + if (publicKey == null) + { + throw new ArgumentNullException(nameof(publicKey)); + } + + this.partnerKey = ToBigInteger(publicKey.PublicComponent); + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + this.disposedValue = true; + } + } + + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup14.cs b/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup14.cs new file mode 100644 index 00000000..785ea442 --- /dev/null +++ b/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup14.cs @@ -0,0 +1,24 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + /// + /// Cross-platform managed Diffie-Hellman key agreement using Oakley Group 14 (2048-bit) + /// per RFC 3526 Section 3. + /// + public class ManagedDiffieHellmanOakleyGroup14 : ManagedDiffieHellman + { + public ManagedDiffieHellmanOakleyGroup14() + : base(Oakley.Group14.Prime, Oakley.Group14.Generator, Oakley.Group14.Factor) + { + } + + public ManagedDiffieHellmanOakleyGroup14(DiffieHellmanKey importKey) + : base(importKey) + { + } + } +} diff --git a/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup2.cs b/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup2.cs new file mode 100644 index 00000000..7f59700c --- /dev/null +++ b/Kerberos.NET/Crypto/Pkinit/ManagedDiffieHellmanOakleyGroup2.cs @@ -0,0 +1,24 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +namespace Kerberos.NET.Crypto +{ + /// + /// Cross-platform managed Diffie-Hellman key agreement using Oakley Group 2 (1024-bit) + /// per RFC 2409. + /// + public class ManagedDiffieHellmanOakleyGroup2 : ManagedDiffieHellman + { + public ManagedDiffieHellmanOakleyGroup2() + : base(Oakley.Group2.Prime, Oakley.Group2.Generator, Oakley.Group2.Factor) + { + } + + public ManagedDiffieHellmanOakleyGroup2(DiffieHellmanKey importKey) + : base(importKey) + { + } + } +} diff --git a/Kerberos.NET/Server/PaDataPkAsReqHandler.cs b/Kerberos.NET/Server/PaDataPkAsReqHandler.cs index 91054222..b8199383 100644 --- a/Kerberos.NET/Server/PaDataPkAsReqHandler.cs +++ b/Kerberos.NET/Server/PaDataPkAsReqHandler.cs @@ -241,9 +241,39 @@ private bool IsSupportedAlgorithm(KeyAgreementAlgorithm algorithm, ReadOnlyMemor return expectedPVal.Span.SequenceEqual(actualPVal.Span); } - private IKeyAgreement FromEllipticCurveDomainParameters(KrbSubjectPublicKeyInfo _) + private IKeyAgreement FromEllipticCurveDomainParameters(KrbSubjectPublicKeyInfo clientPublicValue) { - throw new NotImplementedException(); + // The algorithm parameters contain the curve OID + var curveOid = ExtractCurveOid(clientPublicValue.Algorithm.Parameters.Value); + + var algorithm = EcdhKeyAgreement.FromCurveOid(curveOid); + + if (!this.SupportedKeyAgreementAlgorithms.Contains(algorithm)) + { + throw new InvalidOperationException($"Unsupported EC curve: {curveOid}"); + } + + IKeyAgreement agreement = algorithm switch + { + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256 => CryptoPal.Platform.DiffieHellmanP256(), + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384 => CryptoPal.Platform.DiffieHellmanP384(), + KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521 => CryptoPal.Platform.DiffieHellmanP521(), + _ => throw new InvalidOperationException($"Unsupported ECDH algorithm: {algorithm}") + }; + + var publicKey = EcdhKey.ParsePublicKey(clientPublicValue.SubjectPublicKey, algorithm); + + agreement.ImportPartnerKey(publicKey); + + return agreement; + } + + private static string ExtractCurveOid(ReadOnlyMemory parameters) + { + // The parameters field contains the curve OID encoded as an ASN.1 OBJECT IDENTIFIER + var reader = new System.Security.Cryptography.Asn1.AsnReader(parameters, System.Security.Cryptography.Asn1.AsnEncodingRules.DER); + var oid = reader.ReadObjectIdentifierAsString(); + return oid; } private void ValidateAuthenticator(KrbPKAuthenticator authenticator, KrbKdcReqBody body) diff --git a/Tests/Tests.Kerberos.NET/Crypto/CryptoPalTests.cs b/Tests/Tests.Kerberos.NET/Crypto/CryptoPalTests.cs index 9931a330..895ae3d4 100644 --- a/Tests/Tests.Kerberos.NET/Crypto/CryptoPalTests.cs +++ b/Tests/Tests.Kerberos.NET/Crypto/CryptoPalTests.cs @@ -87,24 +87,33 @@ public void PalSupportsDHModp14() } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] public void PalSupportsECDHP256() { - CryptoPal.Platform.DiffieHellmanP256(); + using (var ecdh = CryptoPal.Platform.DiffieHellmanP256()) + { + Assert.IsNotNull(ecdh); + Assert.IsNotNull(ecdh.PublicKey); + } } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] public void PalSupportsECDHP384() { - CryptoPal.Platform.DiffieHellmanP384(); + using (var ecdh = CryptoPal.Platform.DiffieHellmanP384()) + { + Assert.IsNotNull(ecdh); + Assert.IsNotNull(ecdh.PublicKey); + } } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] public void PalSupportsECDHP521() { - CryptoPal.Platform.DiffieHellmanP521(); + using (var ecdh = CryptoPal.Platform.DiffieHellmanP521()) + { + Assert.IsNotNull(ecdh); + Assert.IsNotNull(ecdh.PublicKey); + } } [TestMethod] diff --git a/Tests/Tests.Kerberos.NET/Crypto/DiffieHellmanKeyAgreementTests.cs b/Tests/Tests.Kerberos.NET/Crypto/DiffieHellmanKeyAgreementTests.cs index 31213cb4..3b035a8b 100644 --- a/Tests/Tests.Kerberos.NET/Crypto/DiffieHellmanKeyAgreementTests.cs +++ b/Tests/Tests.Kerberos.NET/Crypto/DiffieHellmanKeyAgreementTests.cs @@ -202,8 +202,8 @@ public void ImportPrivateKey() [TestMethod] public void ManagedAgreesWithManagedGroup14() { - using (var alice = new ManagedDiffieHellmanOakley14()) - using (var bob = new ManagedDiffieHellmanOakley14()) + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) { alice.ImportPartnerKey(bob.PublicKey); bob.ImportPartnerKey(alice.PublicKey); @@ -216,7 +216,7 @@ public void ManagedAgreesWithManagedGroup14() public void ManagedAgreesWithNativeGroup14() { using (var alice = new BCryptDiffieHellmanOakleyGroup14()) - using (var bob = new ManagedDiffieHellmanOakley14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) { alice.ImportPartnerKey(bob.PublicKey); bob.ImportPartnerKey(alice.PublicKey); @@ -228,8 +228,8 @@ public void ManagedAgreesWithNativeGroup14() [TestMethod] public void ManagedAgreesWithManagedGroup2() { - using (var alice = new ManagedDiffieHellmanOakley2()) - using (var bob = new ManagedDiffieHellmanOakley2()) + using (var alice = new ManagedDiffieHellmanOakleyGroup2()) + using (var bob = new ManagedDiffieHellmanOakleyGroup2()) { alice.ImportPartnerKey(bob.PublicKey); bob.ImportPartnerKey(alice.PublicKey); @@ -242,7 +242,7 @@ public void ManagedAgreesWithManagedGroup2() public void ManagedAgreesWithNativeGroup2() { using (var alice = new BCryptDiffieHellmanOakleyGroup2()) - using (var bob = new ManagedDiffieHellmanOakley2()) + using (var bob = new ManagedDiffieHellmanOakleyGroup2()) { alice.ImportPartnerKey(bob.PublicKey); bob.ImportPartnerKey(alice.PublicKey); @@ -256,8 +256,8 @@ public void ManagedExportAgreeswithNativeImportGroup2() { DiffieHellmanKey managedExport; - using (var alice = new ManagedDiffieHellmanOakley2()) - using (var bob = new ManagedDiffieHellmanOakley2()) + using (var alice = new ManagedDiffieHellmanOakleyGroup2()) + using (var bob = new ManagedDiffieHellmanOakleyGroup2()) { managedExport = alice.PrivateKey as DiffieHellmanKey; @@ -282,7 +282,7 @@ public void ManagedExportAgreeswithNativeImportGroup2() [TestMethod] public void ManagedExportMatchesNativeImport() { - using (var alice = new ManagedDiffieHellmanOakley2()) + using (var alice = new ManagedDiffieHellmanOakleyGroup2()) { var managedExportPrivate = alice.PrivateKey as DiffieHellmanKey; var managedExportPublic = alice.PublicKey as DiffieHellmanKey; diff --git a/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellman.cs b/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellman.cs deleted file mode 100644 index 16449920..00000000 --- a/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellman.cs +++ /dev/null @@ -1,193 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -using System; -using System.Numerics; -using System.Security.Cryptography; - -namespace Kerberos.NET.Crypto -{ - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // DO NOT USE THIS - // - // THIS IS NOT PRODUCTION-WORTHY CODE - // IT IS UNSAFE AND UNTESTED - // - // DO NOT USE THIS - // - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /// - /// DO NOT USE THIS - /// - /// THIS IS NOT PRODUCTION-WORTHY CODE - /// IT IS UNSAFE AND UNTESTED - /// - /// DO NOT USE THIS - /// - public abstract class ManagedDiffieHellman : IKeyAgreement - { - private readonly int keyLength; - - private readonly BigInteger prime; - private readonly BigInteger generator; - private readonly BigInteger factor; - private readonly BigInteger x; - - private readonly BigInteger y; - - private BigInteger partnerKey; - private bool disposedValue; - - public ManagedDiffieHellman(ReadOnlyMemory prime, ReadOnlyMemory generator, ReadOnlyMemory factor) - { - this.keyLength = prime.Length; - - this.prime = ParseBigInteger(prime); - this.generator = ParseBigInteger(generator); - this.factor = ParseBigInteger(factor); - - this.x = this.GeneratePrime(); - - this.y = BigInteger.ModPow(this.generator, this.x, this.prime); - - this.PublicKey = new DiffieHellmanKey - { - Type = AsymmetricKeyType.Public, - Generator = this.Depad(this.generator.ToByteArray(), true), - Modulus = this.Depad(this.prime.ToByteArray(), true), - PublicComponent = this.Depad(this.y.ToByteArray(), true), - Factor = this.Depad(this.factor.ToByteArray(), true), - KeyLength = prime.Length - }; - - this.PrivateKey = new DiffieHellmanKey - { - Type = AsymmetricKeyType.Private, - Generator = this.Depad(this.generator.ToByteArray(), true), - Modulus = this.Depad(this.prime.ToByteArray(), true), - PublicComponent = this.Depad(this.y.ToByteArray(), true), - Factor = this.Depad(this.factor.ToByteArray(), true), - PrivateComponent = this.Depad(this.x.ToByteArray(), true), - KeyLength = prime.Length - }; - } - - private BigInteger GeneratePrime() - { - // RSA's P and Q parameters are prime, but len(P+Q) = keylength - // so generate an RSA key twice as large as required and just - // use P as the prime. - - // P in RSA is a safer prime than primes used in DH so it's - // good enough here, though it's costlier to generate. - - using (var alg = new RSACryptoServiceProvider(this.keyLength * 2 * 8)) - { - var rsa = alg.ExportParameters(true); - - return ParseBigInteger(rsa.P); - } - } - - private static BigInteger ParseBigInteger(ReadOnlyMemory arr, bool reverse = false) - { - var pv = arr.ToArray(); - - if (reverse) - { - Array.Reverse(pv); - } - - if (pv[pv.Length - 1] != 0) - { - var copy = new byte[pv.Length + 1]; - - pv.CopyTo(copy, 0); - - pv = copy; - } - - return new BigInteger(pv); - } - - public IExchangeKey PublicKey { get; } - - public IExchangeKey PrivateKey { get; } - - public ReadOnlyMemory GenerateAgreement() - { - var z = BigInteger.ModPow(this.partnerKey, this.x, this.prime); - - var ag = z.ToByteArray(); - - var agreement = this.Depad(ag, true); - - agreement = Pad(agreement, this.keyLength); - - return agreement; - } - - public void ImportPartnerKey(IExchangeKey publicKey) - { - if (publicKey == null) - { - throw new ArgumentNullException(nameof(publicKey)); - } - - this.partnerKey = ParseBigInteger(publicKey.PublicComponent, true); - } - - private byte[] Depad(byte[] data, bool reverse = false) - { - var mem = new Memory(data); - - for (var i = data.Length - 1; i > 0; i--) - { - if (data[i] == 0 && mem.Length > this.keyLength) - { - mem = mem.Slice(0, i); - } - else - { - break; - } - } - - var arr = mem.ToArray(); - - if (reverse) - { - Array.Reverse(arr); - } - - return arr; - } - - private static byte[] Pad(byte[] agreement, int keyLength) - { - var copy = new byte[keyLength]; - - agreement.CopyTo(copy, keyLength - agreement.Length); - - return copy; - } - - protected virtual void Dispose(bool disposing) - { - if (!this.disposedValue) - { - this.disposedValue = true; - } - } - - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley14.cs b/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley14.cs deleted file mode 100644 index 3e4bac02..00000000 --- a/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley14.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -namespace Kerberos.NET.Crypto -{ - public class ManagedDiffieHellmanOakley14 : ManagedDiffieHellman - { - public ManagedDiffieHellmanOakley14() - : base(Oakley.Group14.PrimeLittleEndian, Oakley.Group14.GeneratorLittleEndian, Oakley.Group14.Factor) - { - } - } -} \ No newline at end of file diff --git a/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley2.cs b/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley2.cs deleted file mode 100644 index 6955b46e..00000000 --- a/Tests/Tests.Kerberos.NET/Crypto/ManagedDiffieHellmanOakley2.cs +++ /dev/null @@ -1,15 +0,0 @@ -// ----------------------------------------------------------------------- -// Licensed to The .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ----------------------------------------------------------------------- - -namespace Kerberos.NET.Crypto -{ - public class ManagedDiffieHellmanOakley2 : ManagedDiffieHellman - { - public ManagedDiffieHellmanOakley2() - : base(Oakley.Group2.PrimeLittleEndian, Oakley.Group2.GeneratorLittleEndian, Oakley.Group2.Factor) - { - } - } -} \ No newline at end of file diff --git a/Tests/Tests.Kerberos.NET/Crypto/PkinitCrossPlatformTests.cs b/Tests/Tests.Kerberos.NET/Crypto/PkinitCrossPlatformTests.cs new file mode 100644 index 00000000..51d9ee4a --- /dev/null +++ b/Tests/Tests.Kerberos.NET/Crypto/PkinitCrossPlatformTests.cs @@ -0,0 +1,526 @@ +// ----------------------------------------------------------------------- +// Licensed to The .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ----------------------------------------------------------------------- + +using System; +using System.Linq; +using Kerberos.NET.Crypto; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Tests.Kerberos.NET +{ + [TestClass] + public class PkinitCrossPlatformTests : BaseTest + { + // ---- Managed DH Group 14 (cross-platform) ---- + + [TestMethod] + public void ManagedDH_Group14_GeneratesKeyPair() + { + using (var dh = new ManagedDiffieHellmanOakleyGroup14()) + { + Assert.IsNotNull(dh.PublicKey); + Assert.IsNotNull(dh.PrivateKey); + Assert.AreEqual(AsymmetricKeyType.Public, dh.PublicKey.Type); + Assert.AreEqual(AsymmetricKeyType.Private, dh.PrivateKey.Type); + Assert.AreEqual(256, dh.PublicKey.KeyLength); // 2048 bits = 256 bytes + } + } + + [TestMethod] + public void ManagedDH_Group14_KeyAgreementProducesSameSecret() + { + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var aliceSecret = alice.GenerateAgreement(); + var bobSecret = bob.GenerateAgreement(); + + Assert.IsTrue(aliceSecret.Span.SequenceEqual(bobSecret.Span)); + } + } + + [TestMethod] + public void ManagedDH_Group14_DifferentKeysProduceDifferentPublicValues() + { + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) + { + // Two independent key generations should produce different public keys + Assert.IsFalse(alice.PublicKey.PublicComponent.Span.SequenceEqual( + bob.PublicKey.PublicComponent.Span)); + } + } + + [TestMethod] + public void ManagedDH_Group14_AgreementLengthIs256Bytes() + { + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var secret = alice.GenerateAgreement(); + Assert.AreEqual(256, secret.Length); // 2048-bit group + } + } + + // ---- Managed DH Group 2 (cross-platform) ---- + + [TestMethod] + public void ManagedDH_Group2_GeneratesKeyPair() + { + using (var dh = new ManagedDiffieHellmanOakleyGroup2()) + { + Assert.IsNotNull(dh.PublicKey); + Assert.IsNotNull(dh.PrivateKey); + Assert.AreEqual(128, dh.PublicKey.KeyLength); // 1024 bits = 128 bytes + } + } + + [TestMethod] + public void ManagedDH_Group2_KeyAgreementProducesSameSecret() + { + using (var alice = new ManagedDiffieHellmanOakleyGroup2()) + using (var bob = new ManagedDiffieHellmanOakleyGroup2()) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var aliceSecret = alice.GenerateAgreement(); + var bobSecret = bob.GenerateAgreement(); + + Assert.IsTrue(aliceSecret.Span.SequenceEqual(bobSecret.Span)); + } + } + + // ---- Managed DH import/export ---- + + [TestMethod] + public void ManagedDH_Group14_ImportExportPrivateKey() + { + ReadOnlyMemory firstAgreement; + DiffieHellmanKey exportedKey; + + // Generate a key and compute agreement + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) + { + exportedKey = (DiffieHellmanKey)alice.PrivateKey; + + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + firstAgreement = alice.GenerateAgreement(); + } + + // Reimport the private key and verify we can reproduce agreement + using (var reimported = new ManagedDiffieHellmanOakleyGroup14(exportedKey)) + { + Assert.IsTrue(reimported.PublicKey.PublicComponent.Span.SequenceEqual( + exportedKey.PublicComponent.Span)); + } + } + + // ---- Managed DH Cross-Platform with BCrypt ---- + + [TestMethod] + public void ManagedDH_Group14_AgreesWithBCrypt() + { + // This is the critical test - managed implementation must produce + // identical shared secrets as the Windows BCrypt implementation + using (var managed = new ManagedDiffieHellmanOakleyGroup14()) + using (var native = new BCryptDiffieHellmanOakleyGroup14()) + { + managed.ImportPartnerKey(native.PublicKey); + native.ImportPartnerKey(managed.PublicKey); + + var managedSecret = managed.GenerateAgreement(); + var nativeSecret = native.GenerateAgreement(); + + Assert.IsTrue(managedSecret.Span.SequenceEqual(nativeSecret.Span), + "Managed and BCrypt DH must produce identical shared secrets"); + } + } + + [TestMethod] + public void ManagedDH_Group2_AgreesWithBCrypt() + { + using (var managed = new ManagedDiffieHellmanOakleyGroup2()) + using (var native = new BCryptDiffieHellmanOakleyGroup2()) + { + managed.ImportPartnerKey(native.PublicKey); + native.ImportPartnerKey(managed.PublicKey); + + var managedSecret = managed.GenerateAgreement(); + var nativeSecret = native.GenerateAgreement(); + + Assert.IsTrue(managedSecret.Span.SequenceEqual(nativeSecret.Span), + "Managed and BCrypt DH must produce identical shared secrets"); + } + } + + // ---- Managed DH Error Cases ---- + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void ManagedDH_GenerateAgreement_WithoutPartnerKey_Throws() + { + using (var dh = new ManagedDiffieHellmanOakleyGroup14()) + { + dh.GenerateAgreement(); + } + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void ManagedDH_ImportPartnerKey_Null_Throws() + { + using (var dh = new ManagedDiffieHellmanOakleyGroup14()) + { + dh.ImportPartnerKey(null); + } + } + + // ---- CryptoPal Integration (cross-platform DH) ---- + + [TestMethod] + public void CryptoPal_DiffieHellmanModp14_DoesNotThrow() + { + // On all platforms, DH-MODP-14 should now work + using (var agreement = CryptoPal.Platform.DiffieHellmanModp14()) + { + Assert.IsNotNull(agreement); + Assert.IsNotNull(agreement.PublicKey); + Assert.IsNotNull(agreement.PrivateKey); + } + } + + [TestMethod] + public void CryptoPal_DiffieHellmanModp2_DoesNotThrow() + { + using (var agreement = CryptoPal.Platform.DiffieHellmanModp2()) + { + Assert.IsNotNull(agreement); + Assert.IsNotNull(agreement.PublicKey); + } + } + + [TestMethod] + public void CryptoPal_DiffieHellmanModp14_WithPrivateKey_DoesNotThrow() + { + IExchangeKey privateKey; + + using (var agreement = CryptoPal.Platform.DiffieHellmanModp14()) + { + privateKey = agreement.PrivateKey; + } + + using (var reimported = CryptoPal.Platform.DiffieHellmanModp14(privateKey)) + { + Assert.IsNotNull(reimported); + } + } + + // ---- ECDH Key Agreement ---- + + [TestMethod] + public void Ecdh_IsSupported_ReturnsTrue() + { + // On .NET 8 runtime, ECDH should be available + Assert.IsTrue(EcdhKeyAgreement.IsSupported, + "ECDH should be supported on .NET 8 runtime"); + } + + [TestMethod] + public void Ecdh_P256_GeneratesKeyPair() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + Assert.IsNotNull(ecdh.PublicKey); + Assert.IsNotNull(ecdh.PrivateKey); + Assert.AreEqual(32, ecdh.PublicKey.KeyLength); // 256-bit = 32-byte coordinates + Assert.AreEqual(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256, ecdh.PublicKey.Algorithm); + } + } + + [TestMethod] + public void Ecdh_P256_PublicKeyIsUncompressedPoint() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + var pubKey = ecdh.PublicKey.PublicComponent; + + // Uncompressed point: 04 || x(32) || y(32) = 65 bytes + Assert.AreEqual(65, pubKey.Length); + Assert.AreEqual(0x04, pubKey.Span[0]); + } + } + + [TestMethod] + public void Ecdh_P384_GeneratesKeyPair() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384)) + { + Assert.IsNotNull(ecdh.PublicKey); + Assert.AreEqual(48, ecdh.PublicKey.KeyLength); // 384-bit = 48-byte coordinates + + var pubKey = ecdh.PublicKey.PublicComponent; + Assert.AreEqual(97, pubKey.Length); // 04 || x(48) || y(48) = 97 + Assert.AreEqual(0x04, pubKey.Span[0]); + } + } + + [TestMethod] + public void Ecdh_P521_GeneratesKeyPair() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521)) + { + Assert.IsNotNull(ecdh.PublicKey); + Assert.AreEqual(66, ecdh.PublicKey.KeyLength); // 521-bit = 66-byte coordinates + + var pubKey = ecdh.PublicKey.PublicComponent; + Assert.AreEqual(133, pubKey.Length); // 04 || x(66) || y(66) = 133 + Assert.AreEqual(0x04, pubKey.Span[0]); + } + } + + [TestMethod] + public void Ecdh_P256_KeyAgreementProducesSameSecret() + { + using (var alice = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + using (var bob = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var aliceSecret = alice.GenerateAgreement(); + var bobSecret = bob.GenerateAgreement(); + + Assert.IsTrue(aliceSecret.Length > 0); + Assert.IsTrue(bobSecret.Length > 0); + Assert.IsTrue(aliceSecret.Span.SequenceEqual(bobSecret.Span), + "ECDH key agreement must produce identical shared secrets on both sides"); + } + } + + [TestMethod] + public void Ecdh_P384_KeyAgreementProducesSameSecret() + { + using (var alice = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384)) + using (var bob = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384)) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var aliceSecret = alice.GenerateAgreement(); + var bobSecret = bob.GenerateAgreement(); + + Assert.IsTrue(aliceSecret.Span.SequenceEqual(bobSecret.Span)); + } + } + + [TestMethod] + public void Ecdh_P521_KeyAgreementProducesSameSecret() + { + using (var alice = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521)) + using (var bob = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521)) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var aliceSecret = alice.GenerateAgreement(); + var bobSecret = bob.GenerateAgreement(); + + Assert.IsTrue(aliceSecret.Span.SequenceEqual(bobSecret.Span)); + } + } + + [TestMethod] + public void Ecdh_DifferentKeysProduceDifferentPublicValues() + { + using (var alice = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + using (var bob = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + Assert.IsFalse(alice.PublicKey.PublicComponent.Span.SequenceEqual( + bob.PublicKey.PublicComponent.Span)); + } + } + + // ---- ECDH Error Cases ---- + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void Ecdh_GenerateAgreement_WithoutPartnerKey_Throws() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + ecdh.GenerateAgreement(); + } + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Ecdh_ImportPartnerKey_Null_Throws() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + ecdh.ImportPartnerKey(null); + } + } + + // ---- EcdhKey ---- + + [TestMethod] + public void EcdhKey_ParsePublicKey_P256() + { + using (var ecdh = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + var encoded = ecdh.PublicKey.PublicComponent; + var parsed = EcdhKey.ParsePublicKey(encoded, KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256); + + Assert.AreEqual(32, parsed.KeyLength); + Assert.AreEqual(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256, parsed.Algorithm); + Assert.IsTrue(parsed.PublicComponent.Span.SequenceEqual(encoded.Span)); + } + } + + // ---- Curve OID Mapping ---- + + [TestMethod] + public void Ecdh_CurveOid_P256() + { + var oid = EcdhKeyAgreement.GetCurveOid(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256); + Assert.AreEqual("1.2.840.10045.3.1.7", oid.Value); + } + + [TestMethod] + public void Ecdh_CurveOid_P384() + { + var oid = EcdhKeyAgreement.GetCurveOid(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384); + Assert.AreEqual("1.3.132.0.34", oid.Value); + } + + [TestMethod] + public void Ecdh_CurveOid_P521() + { + var oid = EcdhKeyAgreement.GetCurveOid(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521); + Assert.AreEqual("1.3.132.0.35", oid.Value); + } + + [TestMethod] + public void Ecdh_FromCurveOid_P256() + { + var alg = EcdhKeyAgreement.FromCurveOid("1.2.840.10045.3.1.7"); + Assert.AreEqual(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256, alg); + } + + [TestMethod] + public void Ecdh_FromCurveOid_P384() + { + var alg = EcdhKeyAgreement.FromCurveOid("1.3.132.0.34"); + Assert.AreEqual(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP384, alg); + } + + [TestMethod] + public void Ecdh_FromCurveOid_P521() + { + var alg = EcdhKeyAgreement.FromCurveOid("1.3.132.0.35"); + Assert.AreEqual(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP521, alg); + } + + // ---- CryptoPal ECDH Integration ---- + + [TestMethod] + public void CryptoPal_DiffieHellmanP256_DoesNotThrow() + { + using (var agreement = CryptoPal.Platform.DiffieHellmanP256()) + { + Assert.IsNotNull(agreement); + Assert.IsNotNull(agreement.PublicKey); + } + } + + [TestMethod] + public void CryptoPal_DiffieHellmanP384_DoesNotThrow() + { + using (var agreement = CryptoPal.Platform.DiffieHellmanP384()) + { + Assert.IsNotNull(agreement); + } + } + + [TestMethod] + public void CryptoPal_DiffieHellmanP521_DoesNotThrow() + { + using (var agreement = CryptoPal.Platform.DiffieHellmanP521()) + { + Assert.IsNotNull(agreement); + } + } + + // ---- PKInitString2Key Integration ---- + + [TestMethod] + public void PKInitString2Key_DH_ProducesExpectedLength() + { + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var sharedSecret = alice.GenerateAgreement(); + + // AES-256 key = 32 bytes + var sessionKey = PKInitString2Key.String2Key(sharedSecret.Span, 32); + Assert.AreEqual(32, sessionKey.Length); + + // AES-128 key = 16 bytes + var sessionKey128 = PKInitString2Key.String2Key(sharedSecret.Span, 16); + Assert.AreEqual(16, sessionKey128.Length); + } + } + + [TestMethod] + public void PKInitString2Key_ECDH_ProducesExpectedLength() + { + using (var alice = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + using (var bob = new EcdhKeyAgreement(KeyAgreementAlgorithm.EllipticCurveDiffieHellmanP256)) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var sharedSecret = alice.GenerateAgreement(); + + var sessionKey = PKInitString2Key.String2Key(sharedSecret.Span, 32); + Assert.AreEqual(32, sessionKey.Length); + } + } + + [TestMethod] + public void PKInitString2Key_SameSecret_ProducesSameKey() + { + using (var alice = new ManagedDiffieHellmanOakleyGroup14()) + using (var bob = new ManagedDiffieHellmanOakleyGroup14()) + { + alice.ImportPartnerKey(bob.PublicKey); + bob.ImportPartnerKey(alice.PublicKey); + + var aliceSecret = alice.GenerateAgreement(); + var bobSecret = bob.GenerateAgreement(); + + var aliceKey = PKInitString2Key.String2Key(aliceSecret.Span, 32); + var bobKey = PKInitString2Key.String2Key(bobSecret.Span, 32); + + Assert.IsTrue(aliceKey.Span.SequenceEqual(bobKey.Span), + "Same shared secret must produce same session key"); + } + } + } +} diff --git a/Tests/Tests.Kerberos.NET/End2End/ClientToKdcE2ETests.cs b/Tests/Tests.Kerberos.NET/End2End/ClientToKdcE2ETests.cs index 555b9982..1a960634 100644 --- a/Tests/Tests.Kerberos.NET/End2End/ClientToKdcE2ETests.cs +++ b/Tests/Tests.Kerberos.NET/End2End/ClientToKdcE2ETests.cs @@ -133,8 +133,7 @@ public async Task PKINIT_Unsupported_KeyAgreement_None() } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] - public async Task PKINIT_Unsupported_KeyAgreement_EC() + public async Task E2E_PKINIT_KeyAgreement_EC() { var port = NextPort(); @@ -153,8 +152,7 @@ public async Task PKINIT_Unsupported_KeyAgreement_EC() } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] - public async Task PKINIT_Unsupported_KeyAgreement_P256() + public async Task E2E_PKINIT_KeyAgreement_P256() { var port = NextPort(); @@ -173,8 +171,7 @@ public async Task PKINIT_Unsupported_KeyAgreement_P256() } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] - public async Task PKINIT_Unsupported_KeyAgreement_P384() + public async Task E2E_PKINIT_KeyAgreement_P384() { var port = NextPort(); @@ -193,8 +190,7 @@ public async Task PKINIT_Unsupported_KeyAgreement_P384() } [TestMethod] - [ExpectedException(typeof(PlatformNotSupportedException))] - public async Task PKINIT_Unsupported_KeyAgreement_P521() + public async Task E2E_PKINIT_KeyAgreement_P521() { var port = NextPort();