From ae8243b18d515b4942e41989b5d78fd05026ccd3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 17 Oct 2021 00:40:40 +0700 Subject: TLS 1.3 PSK server-side work --- crypto/src/tls/AbstractTlsServer.cs | 5 +++++ crypto/src/tls/ClientHello.cs | 5 ++++- crypto/src/tls/HandshakeMessageInput.cs | 36 ++++++++++++++++++++++++++++++++- crypto/src/tls/OfferedPsks.cs | 29 +++++++++++++++++++++----- crypto/src/tls/PskIdentity.cs | 17 ++++++++++++++++ crypto/src/tls/TlsServer.cs | 11 ++++++++++ 6 files changed, 96 insertions(+), 7 deletions(-) diff --git a/crypto/src/tls/AbstractTlsServer.cs b/crypto/src/tls/AbstractTlsServer.cs index 7514e3618..f12233326 100644 --- a/crypto/src/tls/AbstractTlsServer.cs +++ b/crypto/src/tls/AbstractTlsServer.cs @@ -250,6 +250,11 @@ namespace Org.BouncyCastle.Tls return null; } + public virtual TlsPskExternal GetExternalPsk(IList identities) + { + return null; + } + public virtual void NotifySession(TlsSession session) { } diff --git a/crypto/src/tls/ClientHello.cs b/crypto/src/tls/ClientHello.cs index 700d424cd..7f1018e89 100644 --- a/crypto/src/tls/ClientHello.cs +++ b/crypto/src/tls/ClientHello.cs @@ -70,6 +70,9 @@ namespace Org.BouncyCastle.Tls /// public void Encode(TlsContext context, Stream output) { + if (m_bindersSize < 0) + throw new TlsFatalAlert(AlertDescription.internal_error); + TlsUtilities.WriteVersion(m_version, output); output.Write(m_random, 0, m_random.Length); @@ -168,7 +171,7 @@ namespace Org.BouncyCastle.Tls extensions = TlsProtocol.ReadExtensionsDataClientHello(extBytes); } - return new ClientHello(clientVersion, random, sessionID, cookie, cipherSuites, extensions, 0); + return new ClientHello(clientVersion, random, sessionID, cookie, cipherSuites, extensions, -1); } } } diff --git a/crypto/src/tls/HandshakeMessageInput.cs b/crypto/src/tls/HandshakeMessageInput.cs index d7cd19994..c15112cc0 100644 --- a/crypto/src/tls/HandshakeMessageInput.cs +++ b/crypto/src/tls/HandshakeMessageInput.cs @@ -6,17 +6,51 @@ using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Tls { + // TODO Rewrite without MemoryStream public sealed class HandshakeMessageInput : MemoryStream { + private readonly int m_offset; + internal HandshakeMessageInput(byte[] buf, int offset, int length) - : base(buf, offset, length, false) + : base(buf, offset, length, false, true) { +#if PORTABLE + this.m_offset = 0; +#else + this.m_offset = offset; +#endif } public void UpdateHash(TlsHash hash) { Streams.WriteBufTo(this, new TlsHashSink(hash)); } + + internal void UpdateHashPrefix(TlsHash hash, int bindersSize) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Length; +#endif + + hash.Update(buf, m_offset, count - bindersSize); + } + + internal void UpdateHashSuffix(TlsHash hash, int bindersSize) + { +#if PORTABLE + byte[] buf = ToArray(); + int count = buf.Length; +#else + byte[] buf = GetBuffer(); + int count = (int)Length; +#endif + + hash.Update(buf, m_offset + count - bindersSize, bindersSize); + } } } diff --git a/crypto/src/tls/OfferedPsks.cs b/crypto/src/tls/OfferedPsks.cs index 14b6448b4..9eddd2e23 100644 --- a/crypto/src/tls/OfferedPsks.cs +++ b/crypto/src/tls/OfferedPsks.cs @@ -28,21 +28,35 @@ namespace Org.BouncyCastle.Tls private readonly IList m_identities; private readonly IList m_binders; + private readonly int m_bindersSize; public OfferedPsks(IList identities) - : this(identities, null) + : this(identities, null, -1) { } - private OfferedPsks(IList identities, IList binders) + private OfferedPsks(IList identities, IList binders, int bindersSize) { if (null == identities || identities.Count < 1) throw new ArgumentException("cannot be null or empty", "identities"); if (null != binders && identities.Count != binders.Count) throw new ArgumentException("must be the same length as 'identities' (or null)", "binders"); + if ((null != binders) != (bindersSize >= 0)) + throw new ArgumentException("must be >= 0 iff 'binders' are present", "bindersSize"); this.m_identities = identities; this.m_binders = binders; + this.m_bindersSize = bindersSize; + } + + internal byte[] GetBinderForIdentity(PskIdentity matchIdentity) + { + for (int i = 0, count = m_identities.Count; i < count; ++i) + { + if (matchIdentity.Equals(m_identities[i])) + return (byte[])m_binders[i]; + } + return null; } public IList Binders @@ -50,6 +64,11 @@ namespace Org.BouncyCastle.Tls get { return m_binders; } } + public int BindersSize + { + get { return m_bindersSize; } + } + public IList Identities { get { return m_identities; } @@ -168,8 +187,8 @@ namespace Org.BouncyCastle.Tls } IList binders = Platform.CreateArrayList(); + int totalLengthBinders = TlsUtilities.ReadUint16(input); { - int totalLengthBinders = TlsUtilities.ReadUint16(input); if (totalLengthBinders < 33) throw new TlsFatalAlert(AlertDescription.decode_error); @@ -177,13 +196,13 @@ namespace Org.BouncyCastle.Tls MemoryStream buf = new MemoryStream(bindersData, false); do { - byte[] binder = TlsUtilities.ReadOpaque8(input, 32); + byte[] binder = TlsUtilities.ReadOpaque8(buf, 32); binders.Add(binder); } while (buf.Position < buf.Length); } - return new OfferedPsks(identities, binders); + return new OfferedPsks(identities, binders, 2 + totalLengthBinders); } } } diff --git a/crypto/src/tls/PskIdentity.cs b/crypto/src/tls/PskIdentity.cs index 082907419..1887d0af4 100644 --- a/crypto/src/tls/PskIdentity.cs +++ b/crypto/src/tls/PskIdentity.cs @@ -1,6 +1,8 @@ using System; using System.IO; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Tls { public sealed class PskIdentity @@ -48,5 +50,20 @@ namespace Org.BouncyCastle.Tls long obfuscatedTicketAge = TlsUtilities.ReadUint32(input); return new PskIdentity(identity, obfuscatedTicketAge); } + + public override bool Equals(object obj) + { + PskIdentity that = obj as PskIdentity; + if (null == that) + return false; + + return this.m_obfuscatedTicketAge == that.m_obfuscatedTicketAge + && Arrays.ConstantTimeAreEqual(this.m_identity, that.m_identity); + } + + public override int GetHashCode() + { + return Arrays.GetHashCode(m_identity) ^ m_obfuscatedTicketAge.GetHashCode(); + } } } diff --git a/crypto/src/tls/TlsServer.cs b/crypto/src/tls/TlsServer.cs index 783c8c14d..fe88d7c43 100644 --- a/crypto/src/tls/TlsServer.cs +++ b/crypto/src/tls/TlsServer.cs @@ -23,6 +23,17 @@ namespace Org.BouncyCastle.Tls byte[] GetNewSessionID(); + /// Return the external PSK to select from the ClientHello. + /// + /// WARNING: EXPERIMENTAL FEATURE, UNSTABLE API + /// Note that this will only be called when TLS 1.3 or higher is amongst the offered protocol versions, and one + /// or more PSKs are actually offered. + /// + /// an of instances. + /// The corresponding to the selected identity, or null to not select + /// any. + TlsPskExternal GetExternalPsk(IList identities); + void NotifySession(TlsSession session); /// -- cgit 1.4.1