/// <summary> /// Parse a Handshake ServerHello payload from wire format /// </summary> /// <returns> /// True if we successfully decode the ServerHello /// message. Otherwise false. /// </returns> public static bool Parse(out ServerHello result, ByteSpan span) { result = new ServerHello(); if (span.Length < Size) { return(false); } ProtocolVersion serverVersion = (ProtocolVersion)span.ReadBigEndian16(); span = span.Slice(2); result.Random = span.Slice(0, Dtls.Random.Size); span = span.Slice(Dtls.Random.Size); byte sessionKeySize = span[0]; span = span.Slice(1 + sessionKeySize); result.CipherSuite = (CipherSuite)span.ReadBigEndian16(); span = span.Slice(2); CompressionMethod compressionMethod = (CompressionMethod)span[0]; if (compressionMethod != CompressionMethod.Null) { return(false); } return(true); }
/// <summary> /// Parse a Handshake HelloVerifyRequest payload from wire /// format /// </summary> /// <returns> /// True if we successfully decode the HelloVerifyRequest /// message. Otherwise false. /// </returns> public static bool Parse(out HelloVerifyRequest result, ByteSpan span) { result = new HelloVerifyRequest(); if (span.Length < 3) { return(false); } ProtocolVersion serverVersion = (ProtocolVersion)span.ReadBigEndian16(0); if (serverVersion != ProtocolVersion.DTLS1_2) { return(false); } byte cookieSize = span[2]; span = span.Slice(3); if (span.Length < cookieSize) { return(false); } result.Cookie = span; return(true); }
/// <summary> /// Parse a DTLS record from wire format /// </summary> /// <returns>True if we successfully parse the record header. Otherwise false</returns> public static bool Parse(out Record record, ByteSpan span) { record = new Record(); if (span.Length < Size) { return(false); } record.ContentType = (ContentType)span[0]; ProtocolVersion version = (ProtocolVersion)span.ReadBigEndian16(1); record.Epoch = span.ReadBigEndian16(3); record.SequenceNumber = span.ReadBigEndian48(5); record.Length = span.ReadBigEndian16(11); if (version != ProtocolVersion.DTLS1_2) { return(false); } return(true); }
/// <summary> /// Determines if the ClientHello message advertises support /// for the specified curve /// </summary> public bool ContainsCurve(NamedCurve curve) { ByteSpan iterator = this.SupportedCurves; while (iterator.Length >= 2) { if (iterator.ReadBigEndian16() == (ushort)curve) { return(true); } iterator = iterator.Slice(2); } return(false); }
/// <summary> /// Determines if the ClientHello message advertises support /// for the specified cipher suite /// </summary> public bool ContainsCipherSuite(CipherSuite cipherSuite) { ByteSpan iterator = this.CipherSuites; while (iterator.Length >= 2) { if (iterator.ReadBigEndian16() == (ushort)cipherSuite) { return(true); } iterator = iterator.Slice(2); } return(false); }
/// <summary> /// Parse a Handshake protocol header from wire format /// </summary> /// <returns>True if we successfully decode a handshake header. Otherwise false</returns> public static bool Parse(out Handshake header, ByteSpan span) { header = new Handshake(); if (span.Length < Size) { return(false); } header.MessageType = (HandshakeType)span[0]; header.Length = span.ReadBigEndian24(1); header.MessageSequence = span.ReadBigEndian16(4); header.FragmentOffset = span.ReadBigEndian24(6); header.FragmentLength = span.ReadBigEndian24(9); return(true); }
/// <summary> /// Decode a ClientHello extension /// </summary> /// <param name="extensionType">Extension type</param> /// <param name="extensionData">Extension data</param> private void ParseExtension(ExtensionType extensionType, ByteSpan extensionData) { switch (extensionType) { case ExtensionType.EllipticCurves: if (extensionData.Length % 2 != 0) { break; } else if (extensionData.Length < 2) { break; } ushort namedCurveSize = extensionData.ReadBigEndian16(0); if (namedCurveSize % 2 != 0) { break; } this.SupportedCurves = extensionData.Slice(2, namedCurveSize); break; } }
/// <summary> /// Parse a Handshake ClientHello payload from wire format /// </summary> /// <returns>True if we successfully decode the ClientHello message. Otherwise false</returns> public static bool Parse(out ClientHello result, ByteSpan span) { result = new ClientHello(); if (span.Length < MinSize) { return(false); } ProtocolVersion clientVersion = (ProtocolVersion)span.ReadBigEndian16(); if (clientVersion != ProtocolVersion.DTLS1_2) { return(false); } span = span.Slice(2); result.Random = span.Slice(0, Dtls.Random.Size); span = span.Slice(Dtls.Random.Size); ///NOTE(mendsley): We ignore session id byte sessionIdSize = span[0]; if (span.Length < 1 + sessionIdSize) { return(false); } span = span.Slice(1 + sessionIdSize); byte cookieSize = span[0]; if (span.Length < 1 + cookieSize) { return(false); } result.Cookie = span.Slice(1, cookieSize); span = span.Slice(1 + cookieSize); ushort cipherSuiteSize = span.ReadBigEndian16(); if (span.Length < 2 + cipherSuiteSize) { return(false); } else if (cipherSuiteSize % 2 != 0) { return(false); } result.CipherSuites = span.Slice(2, cipherSuiteSize); span = span.Slice(2 + cipherSuiteSize); int compressionMethodsSize = span[0]; bool foundNullCompressionMethod = false; for (int ii = 0; ii != compressionMethodsSize; ++ii) { if (span[1 + ii] == (byte)CompressionMethod.Null) { foundNullCompressionMethod = true; break; } } span = span.Slice(1 + compressionMethodsSize); if (!foundNullCompressionMethod) { return(false); } // Parse extensions if (span.Length > 0) { ushort extensionsSize = span.ReadBigEndian16(); span = span.Slice(2); if (span.Length != extensionsSize) { return(false); } while (span.Length > 0) { // Parse extension header if (span.Length < 4) { return(false); } ExtensionType extensionType = (ExtensionType)span.ReadBigEndian16(0); ushort extensionLength = span.ReadBigEndian16(2); ByteSpan extensionData = span.Slice(4, extensionLength); if (extensionData.Length < extensionLength) { return(false); } span = span.Slice(4 + extensionLength); result.ParseExtension(extensionType, extensionData); } } return(true); }
/// <inheritdoc /> public bool VerifyServerMessageAndGenerateSharedKey(ByteSpan output, ByteSpan serverKeyExchangeMessage, object publicKey) { RSA rsaPublicKey = publicKey as RSA; if (rsaPublicKey == null) { return(false); } else if (output.Length != X25519.KeySize) { return(false); } // Verify message is compatible with this cipher suite if (serverKeyExchangeMessage.Length != CalculateServerMessageSize(rsaPublicKey.KeySize)) { return(false); } else if (serverKeyExchangeMessage[0] != (byte)ECCurveType.NamedCurve) { return(false); } else if (serverKeyExchangeMessage.ReadBigEndian16(1) != (ushort)NamedCurve.x25519) { return(false); } else if (serverKeyExchangeMessage[3] != X25519.KeySize) { return(false); } else if (serverKeyExchangeMessage[4 + X25519.KeySize] != (byte)HashAlgorithm.Sha256) { return(false); } else if (serverKeyExchangeMessage[5 + X25519.KeySize] != (byte)SignatureAlgorithm.RSA) { return(false); } ByteSpan keyParameters = serverKeyExchangeMessage.Slice(0, 4 + X25519.KeySize); ByteSpan othersPublicKey = keyParameters.Slice(4); ushort signatureSize = serverKeyExchangeMessage.ReadBigEndian16(6 + X25519.KeySize); ByteSpan signature = serverKeyExchangeMessage.Slice(4 + keyParameters.Length); if (signatureSize != signature.Length) { return(false); } // Hash the key parameters byte[] parameterDigest = this.sha256.ComputeHash(keyParameters.GetUnderlyingArray(), keyParameters.Offset, keyParameters.Length); // Verify the signature RSAPKCS1SignatureDeformatter verifier = new RSAPKCS1SignatureDeformatter(rsaPublicKey); verifier.SetHashAlgorithm("SHA256"); if (!verifier.VerifySignature(parameterDigest, signature.ToArray())) { return(false); } // Signature has been validated, generate the shared key return(X25519.Func(output, this.privateAgreementKey, othersPublicKey)); }