Ejemplo n.º 1
0
        /// <summary>
        /// Gets UTF-8 text from the given URI.
        /// </summary>
        /// <param name="res">URI and public key for signature verification</param>
        /// <param name="param">Parameters to be sent as <c>application/x-www-form-urlencoded</c> name-value pairs</param>
        /// <param name="token">OAuth access token</param>
        /// <param name="responseType">Expected response MIME type</param>
        /// <param name="previous">Previous content, when refresh is required</param>
        /// <param name="ct">The token to monitor for cancellation requests</param>
        /// <returns>Content</returns>
        public static Response Get(ResourceRef res, NameValueCollection param = null, AccessToken token = null, string responseType = "application/json", Response previous = null, CancellationToken ct = default)
        {
            // Create request.
            var request = WebRequest.Create(res.Uri);

            request.CachePolicy = CachePolicy;
            request.Proxy       = null;
            if (token != null)
            {
                token.AddToRequest(request);
            }
            if (request is HttpWebRequest httpRequest)
            {
                httpRequest.UserAgent = UserAgent;
                httpRequest.Accept    = responseType;
                if (previous != null && param != null)
                {
                    httpRequest.IfModifiedSince = previous.Timestamp;

                    if (previous.ETag != null)
                    {
                        httpRequest.Headers.Add("If-None-Match", previous.ETag);
                    }
                }
            }

            if (param != null)
            {
                // Send data.
                UTF8Encoding utf8    = new UTF8Encoding();
                var          binBody = Encoding.ASCII.GetBytes(string.Join("&", param.Cast <string>().Select(e => string.Format("{0}={1}", HttpUtility.UrlEncode(e, utf8), HttpUtility.UrlEncode(param[e], utf8)))));
                request.Method        = "POST";
                request.ContentType   = "application/x-www-form-urlencoded";
                request.ContentLength = binBody.Length;
                try
                {
                    using (var requestStream = request.GetRequestStream())
                        requestStream.Write(binBody, 0, binBody.Length, ct);
                }
                catch (WebException ex) { throw new AggregateException(Resources.Strings.ErrorUploading, ex.Response is HttpWebResponse ? new WebExceptionEx(ex, ct) : ex); }
            }

            ct.ThrowIfCancellationRequested();

            // Wait for data to start comming in.
            WebResponse response;

            try { response = request.GetResponse(); }
            catch (WebException ex)
            {
                // When the content was not modified, return the previous one.
                if (ex.Response is HttpWebResponse httpResponse)
                {
                    if (httpResponse.StatusCode == HttpStatusCode.NotModified)
                    {
                        previous.IsFresh = false;
                        return(previous);
                    }

                    throw new WebExceptionEx(ex, ct);
                }

                throw new AggregateException(Resources.Strings.ErrorDownloading, ex);
            }

            ct.ThrowIfCancellationRequested();

            using (response)
            {
                // Read the data.
                var data = new byte[0];
                using (var stream = response.GetResponseStream())
                {
                    var buffer = new byte[1048576];
                    for (; ;)
                    {
                        // Read data chunk.
                        var count = stream.Read(buffer, 0, buffer.Length, ct);
                        if (count == 0)
                        {
                            break;
                        }

                        // Append it to the data.
                        var newData = new byte[data.LongLength + count];
                        Array.Copy(data, newData, data.LongLength);
                        Array.Copy(buffer, 0, newData, data.LongLength, count);
                        data = newData;
                    }
                }

                if (res.PublicKeys != null)
                {
                    // Generate signature URI.
                    var uriBuilderSig = new UriBuilder(res.Uri);
                    uriBuilderSig.Path += ".minisig";

                    // Create signature request.
                    request             = WebRequest.Create(uriBuilderSig.Uri);
                    request.CachePolicy = CachePolicy;
                    request.Proxy       = null;
                    if (token != null)
                    {
                        token.AddToRequest(request);
                    }
                    if (request is HttpWebRequest httpRequestSig)
                    {
                        httpRequestSig.UserAgent = UserAgent;
                        httpRequestSig.Accept    = "text/plain";
                    }

                    // Read the Minisign signature.
                    byte[] signature = null;
                    try
                    {
                        using (var responseSig = request.GetResponse())
                            using (var streamSig = responseSig.GetResponseStream())
                            {
                                ct.ThrowIfCancellationRequested();

                                using (var readerSig = new StreamReader(streamSig))
                                {
                                    foreach (var l in readerSig.ReadToEnd(ct).Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries))
                                    {
                                        if (l.Trim().StartsWith($"untrusted comment:"))
                                        {
                                            continue;
                                        }
                                        signature = Convert.FromBase64String(l);
                                        break;
                                    }
                                    if (signature == null)
                                    {
                                        throw new SecurityException(string.Format(Resources.Strings.ErrorInvalidSignature, res.Uri));
                                    }
                                }
                            }
                    }
                    catch (WebException ex) { throw new AggregateException(Resources.Strings.ErrorDownloadingSignature, ex.Response is HttpWebResponse ? new WebExceptionEx(ex, ct) : ex); }

                    ct.ThrowIfCancellationRequested();

                    // Verify Minisign signature.
                    using (var s = new MemoryStream(signature, false))
                        using (var r = new BinaryReader(s))
                        {
                            if (r.ReadChar() != 'E')
                            {
                                throw new ArgumentException(Resources.Strings.ErrorUnsupportedMinisignSignature);
                            }
                            byte[] payload;
                            switch (r.ReadChar())
                            {
                            case 'd': // PureEdDSA
                                payload = data;
                                break;

                            case 'D': // HashedEdDSA
                                payload = new eduEd25519.BLAKE2b(512).ComputeHash(data);
                                break;

                            default:
                                throw new ArgumentException(Resources.Strings.ErrorUnsupportedMinisignSignature);
                            }
                            ulong keyId = r.ReadUInt64();
                            if (!res.PublicKeys.ContainsKey(keyId))
                            {
                                throw new SecurityException(Resources.Strings.ErrorUntrustedMinisignPublicKey);
                            }
                            var sig = new byte[64];
                            if (r.Read(sig, 0, 64) != 64)
                            {
                                throw new ArgumentException(Resources.Strings.ErrorInvalidMinisignSignature);
                            }
                            using (eduEd25519.ED25519 key = new eduEd25519.ED25519(res.PublicKeys[keyId]))
                                if (!key.VerifyDetached(payload, sig))
                                {
                                    throw new SecurityException(string.Format(Resources.Strings.ErrorInvalidSignature, res.Uri));
                                }
                        }
                }

                return
                    (response is HttpWebResponse webResponse ?
                     new Response()
                {
                    Value = Encoding.UTF8.GetString(data),
                    Timestamp = DateTime.TryParse(webResponse.GetResponseHeader("Last-Modified"), out var timestamp) ? timestamp : default,
Ejemplo n.º 2
0
        public static Response Get(Uri uri, NameValueCollection param = null, AccessToken token = null, string response_type = "application/json", byte[] pub_key = null, CancellationToken ct = default(CancellationToken), Response previous = null)
        {
            // Create request.
            var request = WebRequest.Create(uri);

            request.CachePolicy = CachePolicy;
            request.Proxy       = null;
            if (token != null)
            {
                token.AddToRequest(request);
            }
            if (request is HttpWebRequest request_http)
            {
                request_http.UserAgent = UserAgent;
                request_http.Accept    = response_type;
                if (previous != null && param != null)
                {
                    request_http.IfModifiedSince = previous.Timestamp;

                    if (previous.ETag != null)
                    {
                        request_http.Headers.Add("If-None-Match", previous.ETag);
                    }
                }
            }

            if (param != null)
            {
                // Send data.
                UTF8Encoding utf8        = new UTF8Encoding();
                var          body_binary = Encoding.ASCII.GetBytes(String.Join("&", param.Cast <string>().Select(e => String.Format("{0}={1}", HttpUtility.UrlEncode(e, utf8), HttpUtility.UrlEncode(param[e], utf8)))));
                request.Method        = "POST";
                request.ContentType   = "application/x-www-form-urlencoded";
                request.ContentLength = body_binary.Length;
                try
                {
                    using (var stream_req = request.GetRequestStream())
                    {
                        var task = stream_req.WriteAsync(body_binary, 0, body_binary.Length, ct);
                        try { task.Wait(ct); }
                        catch (AggregateException ex) { throw ex.InnerException; }
                    }
                }
                catch (WebException ex) { throw new AggregateException(Resources.Strings.ErrorUploading, ex); }
            }

            ct.ThrowIfCancellationRequested();

            // Wait for data to start comming in.
            WebResponse response;

            try { response = request.GetResponse(); }
            catch (WebException ex)
            {
                // When the content was not modified, return the previous one.
                if (ex.Response is HttpWebResponse response_http)
                {
                    if (response_http.StatusCode == HttpStatusCode.NotModified)
                    {
                        previous.IsFresh = false;
                        return(previous);
                    }

                    // Create our own version of the exception, which will contain the body of response as text.
                    throw new WebExceptionEx(ex, ct);
                }
                else
                {
                    throw new AggregateException(Resources.Strings.ErrorDownloading, ex);
                }
            }

            ct.ThrowIfCancellationRequested();

            using (response)
            {
                // Read the data.
                var data = new byte[0];
                using (var stream = response.GetResponseStream())
                {
                    var buffer = new byte[1048576];
                    for (; ;)
                    {
                        // Read data chunk.
                        var task = stream.ReadAsync(buffer, 0, buffer.Length, ct);
                        try { task.Wait(ct); }
                        catch (AggregateException ex) { throw ex.InnerException; }
                        if (task.Result == 0)
                        {
                            break;
                        }

                        // Append it to the data.
                        var data_new = new byte[data.LongLength + task.Result];
                        Array.Copy(data, data_new, data.LongLength);
                        Array.Copy(buffer, 0, data_new, data.LongLength, task.Result);
                        data = data_new;
                    }
                }

                if (pub_key != null)
                {
                    // Generate signature URI.
                    var builder_sig = new UriBuilder(uri);
                    builder_sig.Path += ".sig";

                    // Create signature request.
                    request             = WebRequest.Create(builder_sig.Uri);
                    request.CachePolicy = CachePolicy;
                    request.Proxy       = null;
                    if (token != null)
                    {
                        token.AddToRequest(request);
                    }
                    if (request is HttpWebRequest request_http_sig)
                    {
                        request_http_sig.UserAgent = UserAgent;
                        request_http_sig.Accept    = "application/pgp-signature";
                    }

                    // Read the signature.
                    byte[] signature = null;
                    using (var response_sig = request.GetResponse())
                        using (var stream_sig = response_sig.GetResponseStream())
                        {
                            ct.ThrowIfCancellationRequested();

                            using (var reader_sig = new StreamReader(stream_sig))
                            {
                                var task = reader_sig.ReadToEndAsync();
                                try { task.Wait(ct); }
                                catch (AggregateException ex) { throw ex.InnerException; }
                                signature = Convert.FromBase64String(task.Result);
                            }
                        }

                    ct.ThrowIfCancellationRequested();

                    // Verify signature.
                    using (eduEd25519.ED25519 key = new eduEd25519.ED25519(pub_key))
                        if (!key.VerifyDetached(data, signature))
                        {
                            throw new System.Security.SecurityException(String.Format(Resources.Strings.ErrorInvalidSignature, uri));
                        }
                }

                return
                    (response is HttpWebResponse response_web ?
                     new Response()
                {
                    Value = Encoding.UTF8.GetString(data),
                    Timestamp = DateTime.TryParse(response_web.GetResponseHeader("Last-Modified"), out var _timestamp) ? _timestamp : default(DateTime),
                    ETag = response_web.GetResponseHeader("ETag"),
                    IsFresh = true
                } :