diff --git a/src/js/operations/Entropy.js b/src/js/operations/Entropy.js index f10cfb5c..33884a66 100755 --- a/src/js/operations/Entropy.js +++ b/src/js/operations/Entropy.js @@ -29,32 +29,12 @@ const Entropy = { entropy = Entropy._calc_entropy(input); output += `Shannon entropy: ${entropy}\n` + - '

\n' + + `

\n` + '- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.\n' + '- Standard English text usually falls somewhere between 3.5 and 5.\n' + '- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.\n\n' + 'The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.\n\n' + - `
`; + '
'; let chunk_entropy = 0; if (chunk_size !== 0) { @@ -109,21 +89,17 @@ const Entropy = { } // Print - let output = `${"
" + - 'Total data length: '}${len - }\nNumber of bytes represented: ${repr - }\nNumber of bytes not represented: ${256 - repr - }\n\nByte Percentage\n` + - ``; + let output = ` +
+ Total data length: ${len} + Number of bytes represented: ${repr} + Number of bytes not represented: ${256 - repr} + + + Byte Percentage +
`; for (i = 0; i < 256; i++) { if (distrib[i] || show_zeroes) { diff --git a/src/js/views/html/OutputWaiter.js b/src/js/views/html/OutputWaiter.js index b8ccac02..a96a12ac 100755 --- a/src/js/views/html/OutputWaiter.js +++ b/src/js/views/html/OutputWaiter.js @@ -1,5 +1,6 @@ import $ from 'jquery'; import Utils from '../../core/Utils'; +import CanvasComponents from './utils/CanvasComponents'; const inlineFuncs = { colorpicker({ rgba }) { @@ -14,6 +15,32 @@ const inlineFuncs = { document.getElementById('input-text').value = css; window.app.auto_bake(); }); + }, + entropy({ entropy }) { + const canvas = document.getElementById('chart-area'); + const parent_rect = canvas.parentNode.getBoundingClientRect(); + const height = parent_rect.height * 0.25; + canvas.width = parent_rect.width * 0.95; + canvas.height = height > 150 ? 150 : height; + CanvasComponents.draw_scale_bar(canvas, entropy, 8, [ + { + label: 'English text', + min: 3.5, + max: 5 + },{ + label: 'Encrypted/compressed', + min: 7.5, + max: 8 + } + ]); + }, + freq({ percentages }) { + const scores = JSON.parse(percentages); + const canvas = document.getElementById('chart-area'); + const parent_rect = canvas.parentNode.getBoundingClientRect(); + canvas.width = parent_rect.width * 0.95; + canvas.height = parent_rect.height * 0.9; + CanvasComponents.draw_bar_chart(canvas, scores, 'Byte', 'Frequency %', 16, 6); } } diff --git a/src/js/views/html/utils/CanvasComponents.js b/src/js/views/html/utils/CanvasComponents.js new file mode 100644 index 00000000..b7edd028 --- /dev/null +++ b/src/js/views/html/utils/CanvasComponents.js @@ -0,0 +1,184 @@ +/** + * Various components for drawing diagrams on an HTML5 canvas. + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + * + * @constant + * @namespace + */ +const CanvasComponents = { + + draw_line: function(ctx, start_x, start_y, end_x, end_y) { + ctx.beginPath(); + ctx.moveTo(start_x, start_y); + ctx.lineTo(end_x, end_y); + ctx.closePath(); + ctx.stroke(); + }, + + draw_bar_chart: function(canvas, scores, x_axis_label, y_axis_label, num_x_labels, num_y_labels, font_size) { + font_size = font_size || 15; + if (!num_x_labels || num_x_labels > Math.round(canvas.width / 50)) { + num_x_labels = Math.round(canvas.width / 50); + } + if (!num_y_labels || num_y_labels > Math.round(canvas.width / 50)) { + num_y_labels = Math.round(canvas.height / 50); + } + + // Graph properties + var ctx = canvas.getContext("2d"), + left_padding = canvas.width * 0.08, + right_padding = canvas.width * 0.03, + top_padding = canvas.height * 0.08, + bottom_padding = canvas.height * 0.15, + graph_height = canvas.height - top_padding - bottom_padding, + graph_width = canvas.width - left_padding - right_padding, + base = top_padding + graph_height, + ceil = top_padding; + + ctx.font = font_size + "px Arial"; + + // Draw axis + ctx.lineWidth = "1.0"; + ctx.strokeStyle = "#444"; + CanvasComponents.draw_line(ctx, left_padding, base, graph_width + left_padding, base); // x + CanvasComponents.draw_line(ctx, left_padding, base, left_padding, ceil); // y + + // Bar properties + var bar_padding = graph_width * 0.003, + bar_width = (graph_width - (bar_padding * scores.length)) / scores.length, + curr_x = left_padding + bar_padding, + max = Math.max.apply(Math, scores); + + // Draw bars + ctx.fillStyle = "green"; + for (var i = 0; i < scores.length; i++) { + var h = scores[i] / max * graph_height; + ctx.fillRect(curr_x, base - h, bar_width, h); + curr_x += bar_width + bar_padding; + } + + // Mark x axis + ctx.fillStyle = "black"; + ctx.textAlign = "center"; + curr_x = left_padding + bar_padding; + if (num_x_labels >= scores.length) { + // Mark every score + for (var i = 0; i <= scores.length; i++) { + ctx.fillText(i, curr_x, base + (bottom_padding * 0.3)); + curr_x += bar_width + bar_padding; + } + } else { + // Mark some scores + for (var i = 0; i <= num_x_labels; i++) { + var val = Math.ceil((scores.length / num_x_labels) * i); + curr_x = (graph_width / num_x_labels) * i + left_padding; + ctx.fillText(val, curr_x, base + (bottom_padding * 0.3)); + } + } + + // Mark y axis + ctx.textAlign = "right"; + var curr_y; + if (num_y_labels >= max) { + // Mark every increment + for (var i = 0; i <= max; i++) { + curr_y = base - (i / max * graph_height) + font_size / 3; + ctx.fillText(i, left_padding * 0.8, curr_y); + } + } else { + // Mark some increments + for (var i = 0; i <= num_y_labels; i++) { + var val = Math.ceil((max / num_y_labels) * i); + curr_y = base - (val / max * graph_height) + font_size / 3; + ctx.fillText(val, left_padding * 0.8, curr_y); + } + } + + // Label x axis + if (x_axis_label) { + ctx.textAlign = "center"; + ctx.fillText(x_axis_label, graph_width / 2 + left_padding, base + bottom_padding * 0.8); + } + + // Label y axis + if (y_axis_label) { + ctx.save(); + var x = left_padding * 0.3, + y = graph_height / 2 + top_padding; + ctx.translate(x, y); + ctx.rotate(-Math.PI / 2); + ctx.textAlign = "center"; + ctx.fillText(y_axis_label, 0, 0); + ctx.restore(); + } + }, + + draw_scale_bar: function(canvas, score, max, markings) { + // Bar properties + var ctx = canvas.getContext("2d"), + left_padding = canvas.width * 0.01, + right_padding = canvas.width * 0.01, + top_padding = canvas.height * 0.1, + bottom_padding = canvas.height * 0.3, + bar_height = canvas.height - top_padding - bottom_padding, + bar_width = canvas.width - left_padding - right_padding; + + // Scale properties + var proportion = score / max; + + // Draw bar outline + ctx.strokeRect(left_padding, top_padding, bar_width, bar_height); + + // Shade in up to proportion + var grad = ctx.createLinearGradient(left_padding, 0, bar_width + left_padding, 0); + grad.addColorStop(0, "green"); + grad.addColorStop(0.5, "gold"); + grad.addColorStop(1, "red"); + ctx.fillStyle = grad; + ctx.fillRect(left_padding, top_padding, bar_width * proportion, bar_height); + + // Add markings + var x0, y0, x1, y1; + ctx.fillStyle = "black"; + ctx.textAlign = "center"; + ctx.font = "13px Arial"; + for (var i = 0; i < markings.length; i++) { + // Draw min line down + x0 = bar_width / max * markings[i].min + left_padding; + y0 = top_padding + bar_height + (bottom_padding * 0.1); + x1 = x0; + y1 = top_padding + bar_height + (bottom_padding * 0.3); + CanvasComponents.draw_line(ctx, x0, y0, x1, y1); + + // Draw max line down + x0 = bar_width / max * markings[i].max + left_padding; + x1 = x0; + CanvasComponents.draw_line(ctx, x0, y0, x1, y1); + + // Join min and max lines + x0 = bar_width / max * markings[i].min + left_padding; + y0 = top_padding + bar_height + (bottom_padding * 0.3); + x1 = bar_width / max * markings[i].max + left_padding; + y1 = y0; + CanvasComponents.draw_line(ctx, x0, y0, x1, y1); + + // Add label + if (markings[i].max >= max * 0.9) { + ctx.textAlign = "right"; + x0 = x1; + } else if (markings[i].max <= max * 0.1) { + ctx.textAlign = "left"; + } else { + x0 = x0 + (x1 - x0) / 2; + } + y0 = top_padding + bar_height + (bottom_padding * 0.8); + ctx.fillText(markings[i].label, x0, y0); + } + }, + +}; + +export default CanvasComponents;