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
/// <exception cref="IOException"/>
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();
+ /// <summary>Return the <see cref="TlsPskExternal">external PSK</see> to select from the ClientHello.</summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ /// <param name="identities">an <see cref="IList"/> of <see cref="PskIdentity"/> instances.</param>
+ /// <returns>The <see cref="TlsPskExternal"/> corresponding to the selected identity, or null to not select
+ /// any.</returns>
+ TlsPskExternal GetExternalPsk(IList identities);
+
void NotifySession(TlsSession session);
/// <exception cref="IOException"/>
|