Skip to content

Instantly share code, notes, and snippets.

@guiguetz
Created January 11, 2018 07:07
Show Gist options
  • Save guiguetz/53ede8f72ca52a2f8d5e7cdc1b60e0a9 to your computer and use it in GitHub Desktop.
Save guiguetz/53ede8f72ca52a2f8d5e7cdc1b60e0a9 to your computer and use it in GitHub Desktop.
Caesar's Cipher

Caesar's Cipher

A simple encoder/decoder using Caesar's Cipher algorithm, where you can dynamically change the shift amount. It's also made with flexbox, so, assured to be responsive in most of the screens!

A Pen by Guilherme Aguiar on CodePen.

License.

<div class="container" ng-app="app" ng-controller="caesarController">
<div class="input">
<form name="inputForm">
<div class="row">
<label for="shiftKey">Shift Key:</label>
<input type="number" min="1" max="25" id="shiftKey" name="shiftKey" ng-model="shiftKey" integer required>
</div>
<div class="row">
<label for="inputText">Input:</label>
<textarea id="inputText" name="inputText" ng-model="inputText" cols="40" rows="7" required></textarea>
</div>
<div class="row button">
<button ng-click="inputForm.$valid && shift(1)">Encode</button>
<button ng-click="crack()">Crack</button>
<button ng-click="inputForm.$valid && shift(-1)">Decode</button>
</div>
</form>
</div>
<div class="output">
<div class="row">
<label for="outputText">Output:</label>
<textarea id="outputText" name="outputText" ng-model="outputText" cols="40" rows="7"></textarea>
</div>
<div class="row button">
<button onclick="copyToClipboard('.output > .row > textarea')">Copy</button>
<button ng-click="sendToInput()">Send to Input</button>
</div>
</div>
<div class="output" ng-hide="!tries.length">
<div class="row">
<h3>Entropy Levels:</h3>
<div ng-repeat="n in tries track by $index">
<div class="progress-bar-indication">
<span class="meter" ng-style="{width: (n[1] / tries[tries.length - 1][1] * 100).toFixed(6) + '%'}">
<p>Shift {{26 - n[0]}}: {{n[1].toFixed(3)}}</p>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<p>2016 - Made by <a href="http://guiguetz.github.io/">Guilherme Aguiar</a>.</p>
</div>
// App Declaration
let app = angular.module('app', []);
/***/
// Controller dedicated to Caesar's cipher
app.controller('caesarController', function($scope) {
$scope.inputText = "Distantes estão os caminhos que vão para o Tempo — outro luar eu vi passar na altura\nNas plagas verdes as mesmas lamentações escuto como vindas da eterna espera\nO vento ríspido agita sombras de araucárias em corpos nus unidos se amando\nE no meu ser todas as agitações se anulam como as vozes dos campos moribundos.";
$scope.shiftKey = 1;
$scope.shift = function(v) {
$scope.outputText = caesarShift(removeAccents($scope.inputText), $scope.shiftKey * v);
};
$scope.sendToInput = function() {
$scope.inputText = $scope.outputText;
};
$scope.crack = function() {
$scope.tries = crackShift($scope.inputText);
let bestShift = 26 - $scope.tries[0][0];
$scope.outputText = caesarShift(removeAccents($scope.inputText), -bestShift);
};
});
// Directive to validate integer number at shift
let INTEGER_REGEXP = /^-?\d+$/;
app.directive('integer', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$validators.integer = function(modelValue, viewValue) {
if (ctrl.$isEmpty(modelValue)) {
// consider empty models to be valid
return true;
}
if (INTEGER_REGEXP.test(viewValue)) {
// it is valid
return true;
}
// it is invalid
return false;
};
}
};
});
function caesarShift(str, key) {
if (key < 0)
return caesarShift(str, key + 26);
let output = '';
for (let i = 0; i < str.length; i ++) {
let c = str[i];
if (c.match(/[a-z]/i)) {
let code = str.charCodeAt(i);
if ((code >= 65) && (code <= 90))
c = String.fromCharCode(((code - 65 + key) % 26) + 65);
else if ((code >= 97) && (code <= 122))
c = String.fromCharCode(((code - 97 + key) % 26) + 97);
}
output += c;
}
return output;
}
function crackShift(str) {
let res = getAllEntropies(str);
res.sort((x, y) => {
if (x[1] < y[1]) return -1;
else if (x[1] > y[1]) return 1;
else if (x[0] < y[0]) return -1;
else if (x[0] > y[0]) return 1;
return 0;
});
let bestShift = 26 - res[0][0];
return res;
}
function getEntropy(str) {
let freqs = [0.1463, 0.0104, 0.0388, 0.0499, 0.1257, 0.0102, 0.0130, 0.0128, 0.0618, 0.0040, 0.0002, 0.0278, 0.0474,0.0505, 0.1073, 0.0252, 0.0120, 0.0653, 0.0781, 0.0434, 0.0463, 0.0167, 0.0001, 0.0021, 0.0001, 0.0047];
let sum = 0;
let ignored = 0;
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
if (c >= 65 && c <= 90) sum += Math.log(freqs[c - 65]); // Uppercase
else if (c >= 97 && c <= 122) sum += Math.log(freqs[c - 97]); // Lowercase
else ignored++;
}
return -sum / Math.log(2) / (str.length - ignored);
}
function getAllEntropies(str) {
let result = new Array(26);
for (let i = 0; i < 26; i++)
result[i] = [i, getEntropy(caesarShift(str, i))];
return result;
}
// Utils
function copyToClipboard(element) {
let $temp = $("<textarea>");
$("body").append($temp);
$temp.val($(element).val()).select();
document.execCommand("copy");
$temp.remove();
}
function removeAccents(str) {
let accents = 'ÀÁÂÃÄÅàáâãäåßÒÓÔÕÕÖØòóôõöøĎďDŽdžÈÉÊËèéêëðÇçČčÐÌÍÎÏìíîïÙÚÛÜùúûüĽĹľĺÑŇňñŔ੹ŤťŸÝÿýŽž';
let accentsOut = "AAAAAAaaaaaasOOOOOOOooooooDdDZdzEEEEeeeeeCcCcDIIIIiiiiUUUUuuuuLLllNNnnRrSsTtYYyyZz";
str = str.split('');
let strLen = str.length;
let x;
for (let i=0; i<strLen; i++) {
if ((x = accents.indexOf(str[i])) != -1) {
str[i] = accentsOut[x];
}
}
return str.join('');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular.js"></script>
@import "bourbon@5.*"
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700")
$default-color: #02556E
$button-color: #2E2E2E
$text-color: #BDD2D9
$error-color: #fbe3e4
$success-color: #e6efc2
%flex-container
display: flex
flex-direction: column
flex-wrap: wrap
justify-content: space-evenly
align-items: stretch
@media (max-width: 768px)
justify-content: center
body
background-color: $default-color
@extend %flex-container
flex-direction: row
align-items: stretch
height: 100vh
font-family: 'Open Sans', sans-serif
color: $text-color
a
color: $text-color
text-decoration: none
&:hover
color: tint($text-color, 80%)
.footer
@extend %flex-container
.ng-invalid.ng-touched
background-color: $error-color
.container
@extend %flex-container
width: 100%
@media (max-width: 768px)
align-items: center
.input,.output
background-color: rgba(white, 0.1)
@extend %flex-container
margin: 1.5%
padding: 2.5%
border-radius: 15px
@media (max-width: 768px)
width: 85%
.row,.button
@extend %flex-container
.button
flex-direction: row
button
background-color: $button-color
width: 150px
padding: 10px
margin-top: 20px
border-radius: 10px
border: none
color: $text-color
font-weight: 700
&:hover
background-color: tint($button-color, 5%)
.progress-bar-indication
$base-border-color: gainsboro !default
$base-border-radius: 3px !default
$base-background-color: white !default
$base-line-height: 1.5em !default
$action-color: #477DCA !default
$progress-border-color: $base-border-color
$progress-border: 1px solid $progress-border-color
$progress-meter-border-color: $action-color
$progress-meter-border: 1px solid darken($progress-meter-border-color, 15%)
$progress-meter-color: $progress-meter-border-color
$progress-background: darken($base-background-color, 5)
$progress-animation-duration: 0.7s
$progress-color: white
background-color: $progress-background
border-radius: $base-border-radius
border: $progress-border
box-shadow: inset 0 0 3px 0 rgba(darken($progress-background, 50%), 0.15)
margin: 2px auto
width: 100%
span.meter
background-color: $progress-meter-color
background-repeat: repeat-x
background-size: 40px 40px
border: $progress-meter-border
border-bottom-right-radius: 0
border-radius: $base-border-radius /1.5
border-top-right-radius: 0
box-sizing: border-box
display: block
height: $base-line-height + 0.3
p
color: $progress-color
line-height: $base-line-height
margin: 0
padding: 0.1em 0.5em
text-shadow: 0 0 1px black
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment