Initial data commit
This commit is contained in:
parent
72d218235f
commit
f23f22d71c
199087 changed files with 3378941 additions and 0 deletions
5
Task/One-time-pad/00-META.yaml
Normal file
5
Task/One-time-pad/00-META.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
category:
|
||||
- Encryption
|
||||
- File_handling
|
||||
from: http://rosettacode.org/wiki/One-time_pad
|
||||
40
Task/One-time-pad/00-TASK.txt
Normal file
40
Task/One-time-pad/00-TASK.txt
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
Implement a [[wp:One-time pad|One-time pad]], for encrypting and decrypting messages.<br>
|
||||
To keep it simple, we will be using letters only.
|
||||
|
||||
;Sub-Tasks:
|
||||
* '''Generate''' the data for a One-time pad (user needs to specify a filename and length)
|
||||
: The important part is to get "true random" numbers, e.g. from /dev/random
|
||||
* '''encryption / decryption''' ( basically the same operation, much like [[Rot-13]] )
|
||||
: For this step, much of [[Vigenère cipher]] could be reused,<br>with the key to be read from the file containing the One-time pad.
|
||||
* optional: '''management''' of One-time pads: list, mark as used, delete, etc.
|
||||
: Somehow, the users needs to keep track which pad to use for which partner.
|
||||
|
||||
To support the management of pad-files:
|
||||
* Such files have a file-extension ".1tp"
|
||||
* Lines starting with "#" may contain arbitary meta-data (i.e. comments)
|
||||
* Lines starting with "-" count as "used"
|
||||
* Whitespace within the otp-data is ignored
|
||||
<!--
|
||||
maybe support for 1tp-files on readonly-media,
|
||||
i.e. an indexfile that stores which parts have been used
|
||||
-->
|
||||
|
||||
|
||||
For example, here is the data from [http://upload.wikimedia.org/wikipedia/commons/6/60/One-time_pad.svg Wikipedia]:
|
||||
<pre>
|
||||
# Example data - Wikipedia - 2014-11-13
|
||||
-ZDXWWW EJKAWO FECIFE WSNZIP PXPKIY URMZHI JZTLBC YLGDYJ
|
||||
-HTSVTV RRYYEG EXNCGA GGQVRF FHZCIB EWLGGR BZXQDQ DGGIAK
|
||||
YHJYEQ TDLCQT HZBSIZ IRZDYS RBYJFZ AIRCWI UCVXTW YKPQMK
|
||||
CKHVEX VXYVCS WOGAAZ OUVVON GCNEVR LMBLYB SBDCDC PCGVJX
|
||||
QXAUIP PXZQIJ JIUWYH COVWMJ UZOJHL DWHPER UBSRUJ HGAAPR
|
||||
CRWVHI FRNTQW AJVWRT ACAKRD OZKIIB VIQGBK IJCWHF GTTSSE
|
||||
EXFIPJ KICASQ IOUQTP ZSGXGH YTYCTI BAZSTN JKMFXI RERYWE
|
||||
</pre>
|
||||
<br><br>
|
||||
;See also
|
||||
* [https://breakingcode.wordpress.com/2010/02/17/one-time-pad-encryption-in-python/ one time pad encryption in Python]
|
||||
* [https://github.com/snapfractalpop/1tp snapfractalpop] - One-Time-Pad Command-Line-Utility (C).
|
||||
* [http://search.cpan.org/~sifukurt/Crypt-OTP-2.00/OTP.pm Crypt-OTP-2.00] on CPAN (Perl)
|
||||
<br><br>
|
||||
|
||||
344
Task/One-time-pad/Go/one-time-pad.go
Normal file
344
Task/One-time-pad/Go/one-time-pad.go
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
charsPerLine = 48
|
||||
chunkSize = 6
|
||||
cols = 8
|
||||
demo = true // would normally be set to false
|
||||
)
|
||||
|
||||
type fileType int
|
||||
|
||||
const (
|
||||
otp fileType = iota
|
||||
enc
|
||||
dec
|
||||
)
|
||||
|
||||
var scnr = bufio.NewScanner(os.Stdin)
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func toAlpha(s string) string {
|
||||
var filtered []rune
|
||||
for _, r := range s {
|
||||
if unicode.IsUpper(r) {
|
||||
filtered = append(filtered, r)
|
||||
}
|
||||
}
|
||||
return string(filtered)
|
||||
}
|
||||
|
||||
func isOtpRelated(s string) bool {
|
||||
return strings.HasSuffix(s, ".1tp") || strings.HasSuffix(s, "1tp_cpy") ||
|
||||
strings.HasSuffix(s, ".1tp_enc") || strings.HasSuffix(s, "1tp_dec")
|
||||
}
|
||||
|
||||
func makePad(nLines int) string {
|
||||
nChars := nLines * charsPerLine
|
||||
bytes := make([]byte, nChars)
|
||||
/* generate random upper case letters */
|
||||
max := big.NewInt(26)
|
||||
for i := 0; i < nChars; i++ {
|
||||
n, err := rand.Int(rand.Reader, max)
|
||||
check(err)
|
||||
bytes[i] = byte(65 + n.Uint64())
|
||||
}
|
||||
return inChunks(string(bytes), nLines, otp)
|
||||
}
|
||||
|
||||
func vigenere(text, key string, encrypt bool) string {
|
||||
bytes := make([]byte, len(text))
|
||||
var ci byte
|
||||
for i, c := range text {
|
||||
if encrypt {
|
||||
ci = (byte(c) + key[i] - 130) % 26
|
||||
} else {
|
||||
ci = (byte(c) + 26 - key[i]) % 26
|
||||
}
|
||||
bytes[i] = ci + 65
|
||||
}
|
||||
temp := len(bytes) % charsPerLine
|
||||
if temp > 0 { // pad with random characters so each line is a full one
|
||||
max := big.NewInt(26)
|
||||
for i := temp; i < charsPerLine; i++ {
|
||||
n, err := rand.Int(rand.Reader, max)
|
||||
check(err)
|
||||
bytes = append(bytes, byte(65+n.Uint64()))
|
||||
}
|
||||
}
|
||||
ft := enc
|
||||
if !encrypt {
|
||||
ft = dec
|
||||
}
|
||||
return inChunks(string(bytes), len(bytes)/charsPerLine, ft)
|
||||
}
|
||||
|
||||
func inChunks(s string, nLines int, ft fileType) string {
|
||||
nChunks := len(s) / chunkSize
|
||||
remainder := len(s) % chunkSize
|
||||
chunks := make([]string, nChunks)
|
||||
for i := 0; i < nChunks; i++ {
|
||||
chunks[i] = s[i*chunkSize : (i+1)*chunkSize]
|
||||
}
|
||||
if remainder > 0 {
|
||||
chunks = append(chunks, s[nChunks*chunkSize:])
|
||||
}
|
||||
var sb strings.Builder
|
||||
for i := 0; i < nLines; i++ {
|
||||
j := i * cols
|
||||
sb.WriteString(" " + strings.Join(chunks[j:j+cols], " ") + "\n")
|
||||
}
|
||||
ss := " file\n" + sb.String()
|
||||
switch ft {
|
||||
case otp:
|
||||
return "# OTP" + ss
|
||||
case enc:
|
||||
return "# Encrypted" + ss
|
||||
default: // case dec:
|
||||
return "# Decrypted" + ss
|
||||
}
|
||||
}
|
||||
|
||||
func menu() int {
|
||||
fmt.Println(`
|
||||
1. Create one time pad file.
|
||||
|
||||
2. Delete one time pad file.
|
||||
|
||||
3. List one time pad files.
|
||||
|
||||
4. Encrypt plain text.
|
||||
|
||||
5. Decrypt cipher text.
|
||||
|
||||
6. Quit program.
|
||||
`)
|
||||
choice := 0
|
||||
for choice < 1 || choice > 6 {
|
||||
fmt.Print("Your choice (1 to 6) : ")
|
||||
scnr.Scan()
|
||||
choice, _ = strconv.Atoi(scnr.Text())
|
||||
check(scnr.Err())
|
||||
}
|
||||
return choice
|
||||
}
|
||||
|
||||
func main() {
|
||||
for {
|
||||
choice := menu()
|
||||
fmt.Println()
|
||||
switch choice {
|
||||
case 1: // Create OTP
|
||||
fmt.Println("Note that encrypted lines always contain 48 characters.\n")
|
||||
fmt.Print("OTP file name to create (without extension) : ")
|
||||
scnr.Scan()
|
||||
fileName := scnr.Text() + ".1tp"
|
||||
nLines := 0
|
||||
for nLines < 1 || nLines > 1000 {
|
||||
fmt.Print("Number of lines in OTP (max 1000) : ")
|
||||
scnr.Scan()
|
||||
nLines, _ = strconv.Atoi(scnr.Text())
|
||||
}
|
||||
check(scnr.Err())
|
||||
key := makePad(nLines)
|
||||
file, err := os.Create(fileName)
|
||||
check(err)
|
||||
_, err = file.WriteString(key)
|
||||
check(err)
|
||||
file.Close()
|
||||
fmt.Printf("\n'%s' has been created in the current directory.\n", fileName)
|
||||
if demo {
|
||||
// a copy of the OTP file would normally be on a different machine
|
||||
fileName2 := fileName + "_cpy" // copy for decryption
|
||||
file, err := os.Create(fileName2)
|
||||
check(err)
|
||||
_, err = file.WriteString(key)
|
||||
check(err)
|
||||
file.Close()
|
||||
fmt.Printf("'%s' has been created in the current directory.\n", fileName2)
|
||||
fmt.Println("\nThe contents of these files are :\n")
|
||||
fmt.Println(key)
|
||||
}
|
||||
case 2: // Delete OTP
|
||||
fmt.Println("Note that this will also delete ALL associated files.\n")
|
||||
fmt.Print("OTP file name to delete (without extension) : ")
|
||||
scnr.Scan()
|
||||
toDelete1 := scnr.Text() + ".1tp"
|
||||
check(scnr.Err())
|
||||
toDelete2 := toDelete1 + "_cpy"
|
||||
toDelete3 := toDelete1 + "_enc"
|
||||
toDelete4 := toDelete1 + "_dec"
|
||||
allToDelete := []string{toDelete1, toDelete2, toDelete3, toDelete4}
|
||||
deleted := 0
|
||||
fmt.Println()
|
||||
for _, name := range allToDelete {
|
||||
if _, err := os.Stat(name); !os.IsNotExist(err) {
|
||||
err = os.Remove(name)
|
||||
check(err)
|
||||
deleted++
|
||||
fmt.Printf("'%s' has been deleted from the current directory.\n", name)
|
||||
}
|
||||
}
|
||||
if deleted == 0 {
|
||||
fmt.Println("There are no files to delete.")
|
||||
}
|
||||
case 3: // List OTPs
|
||||
fmt.Println("The OTP (and related) files in the current directory are:\n")
|
||||
files, err := ioutil.ReadDir(".") // already sorted by file name
|
||||
check(err)
|
||||
for _, fi := range files {
|
||||
name := fi.Name()
|
||||
if !fi.IsDir() && isOtpRelated(name) {
|
||||
fmt.Println(name)
|
||||
}
|
||||
}
|
||||
case 4: // Encrypt
|
||||
fmt.Print("OTP file name to use (without extension) : ")
|
||||
scnr.Scan()
|
||||
keyFile := scnr.Text() + ".1tp"
|
||||
if _, err := os.Stat(keyFile); !os.IsNotExist(err) {
|
||||
file, err := os.Open(keyFile)
|
||||
check(err)
|
||||
bytes, err := ioutil.ReadAll(file)
|
||||
check(err)
|
||||
file.Close()
|
||||
lines := strings.Split(string(bytes), "\n")
|
||||
le := len(lines)
|
||||
first := le
|
||||
for i := 0; i < le; i++ {
|
||||
if strings.HasPrefix(lines[i], " ") {
|
||||
first = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if first == le {
|
||||
fmt.Println("\nThat file has no unused lines.")
|
||||
continue
|
||||
}
|
||||
lines2 := lines[first:] // get rid of comments and used lines
|
||||
|
||||
fmt.Println("Text to encrypt :-\n")
|
||||
scnr.Scan()
|
||||
text := toAlpha(strings.ToUpper(scnr.Text()))
|
||||
check(scnr.Err())
|
||||
tl := len(text)
|
||||
nLines := tl / charsPerLine
|
||||
if tl%charsPerLine > 0 {
|
||||
nLines++
|
||||
}
|
||||
if len(lines2) >= nLines {
|
||||
key := toAlpha(strings.Join(lines2[0:nLines], ""))
|
||||
encrypted := vigenere(text, key, true)
|
||||
encFile := keyFile + "_enc"
|
||||
file2, err := os.Create(encFile)
|
||||
check(err)
|
||||
_, err = file2.WriteString(encrypted)
|
||||
check(err)
|
||||
file2.Close()
|
||||
fmt.Printf("\n'%s' has been created in the current directory.\n", encFile)
|
||||
for i := first; i < first+nLines; i++ {
|
||||
lines[i] = "-" + lines[i][1:]
|
||||
}
|
||||
file3, err := os.Create(keyFile)
|
||||
check(err)
|
||||
_, err = file3.WriteString(strings.Join(lines, "\n"))
|
||||
check(err)
|
||||
file3.Close()
|
||||
if demo {
|
||||
fmt.Println("\nThe contents of the encrypted file are :\n")
|
||||
fmt.Println(encrypted)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Not enough lines left in that file to do encryption.")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("\nThat file does not exist.")
|
||||
}
|
||||
case 5: // Decrypt
|
||||
fmt.Print("OTP file name to use (without extension) : ")
|
||||
scnr.Scan()
|
||||
keyFile := scnr.Text() + ".1tp_cpy"
|
||||
check(scnr.Err())
|
||||
if _, err := os.Stat(keyFile); !os.IsNotExist(err) {
|
||||
file, err := os.Open(keyFile)
|
||||
check(err)
|
||||
bytes, err := ioutil.ReadAll(file)
|
||||
check(err)
|
||||
file.Close()
|
||||
keyLines := strings.Split(string(bytes), "\n")
|
||||
le := len(keyLines)
|
||||
first := le
|
||||
for i := 0; i < le; i++ {
|
||||
if strings.HasPrefix(keyLines[i], " ") {
|
||||
first = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if first == le {
|
||||
fmt.Println("\nThat file has no unused lines.")
|
||||
continue
|
||||
}
|
||||
keyLines2 := keyLines[first:] // get rid of comments and used lines
|
||||
|
||||
encFile := keyFile[0:len(keyFile)-3] + "enc"
|
||||
if _, err := os.Stat(encFile); !os.IsNotExist(err) {
|
||||
file2, err := os.Open(encFile)
|
||||
check(err)
|
||||
bytes, err := ioutil.ReadAll(file2)
|
||||
check(err)
|
||||
file2.Close()
|
||||
encLines := strings.Split(string(bytes), "\n")[1:] // exclude comment line
|
||||
nLines := len(encLines)
|
||||
if len(keyLines2) >= nLines {
|
||||
encrypted := toAlpha(strings.Join(encLines, ""))
|
||||
key := toAlpha(strings.Join(keyLines2[0:nLines], ""))
|
||||
decrypted := vigenere(encrypted, key, false)
|
||||
decFile := keyFile[0:len(keyFile)-3] + "dec"
|
||||
file3, err := os.Create(decFile)
|
||||
check(err)
|
||||
_, err = file3.WriteString(decrypted)
|
||||
check(err)
|
||||
file3.Close()
|
||||
fmt.Printf("\n'%s' has been created in the current directory.\n", decFile)
|
||||
for i := first; i < first+nLines; i++ {
|
||||
keyLines[i] = "-" + keyLines[i][1:]
|
||||
}
|
||||
file4, err := os.Create(keyFile)
|
||||
check(err)
|
||||
_, err = file4.WriteString(strings.Join(keyLines, "\n"))
|
||||
check(err)
|
||||
file4.Close()
|
||||
if demo {
|
||||
fmt.Println("\nThe contents of the decrypted file are :\n")
|
||||
fmt.Println(decrypted)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Not enough lines left in that file to do decryption.")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("\nThat file does not exist.")
|
||||
}
|
||||
case 6: // Quit program
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
172
Task/One-time-pad/Haskell/one-time-pad.hs
Normal file
172
Task/One-time-pad/Haskell/one-time-pad.hs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
-- To compile into an executable:
|
||||
-- ghc -main-is OneTimePad OneTimePad.hs
|
||||
-- To run:
|
||||
-- ./OneTimePad --help
|
||||
|
||||
module OneTimePad (main) where
|
||||
|
||||
import Control.Monad
|
||||
import Data.Char
|
||||
import Data.Function (on)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as TI
|
||||
import Data.Time
|
||||
import System.Console.GetOpt
|
||||
import System.Environment
|
||||
import System.Exit
|
||||
import System.IO
|
||||
|
||||
-- Command-line options parsing
|
||||
data Options = Options { optCommand :: String
|
||||
, optInput :: IO T.Text
|
||||
, optOutput :: T.Text -> IO ()
|
||||
, optPad :: (IO T.Text, T.Text -> IO ())
|
||||
, optLines :: Int
|
||||
}
|
||||
|
||||
startOptions :: Options
|
||||
startOptions = Options { optCommand = "decrypt"
|
||||
, optInput = TI.getContents
|
||||
, optOutput = TI.putStr
|
||||
, optPad = (TI.getContents, TI.putStr)
|
||||
, optLines = 0
|
||||
}
|
||||
|
||||
options :: [ OptDescr (Options -> IO Options) ]
|
||||
options =
|
||||
[ Option "e" ["encrypt"]
|
||||
(NoArg
|
||||
(\opt -> return opt { optCommand = "encrypt" }))
|
||||
"Encrypt file"
|
||||
, Option "d" ["decrypt"]
|
||||
(NoArg
|
||||
(\opt -> return opt { optCommand = "decrypt" }))
|
||||
"Decrypt file (default)"
|
||||
, Option "g" ["generate"]
|
||||
(NoArg
|
||||
(\opt -> return opt { optCommand = "generate" }))
|
||||
"Generate a one-time pad"
|
||||
, Option "i" ["input"]
|
||||
(ReqArg
|
||||
(\arg opt -> return opt { optInput = TI.readFile arg })
|
||||
"FILE")
|
||||
"Input file (for decryption and encryption)"
|
||||
, Option "o" ["output"]
|
||||
(ReqArg
|
||||
(\arg opt -> return opt { optOutput = TI.writeFile arg })
|
||||
"FILE")
|
||||
"Output file (for generation, decryption, and encryption)"
|
||||
, Option "p" ["pad"]
|
||||
(ReqArg
|
||||
(\arg opt -> return opt { optPad = (TI.readFile arg,
|
||||
TI.writeFile arg) })
|
||||
"FILE")
|
||||
"One-time pad to use (for decryption and encryption)"
|
||||
, Option "l" ["lines"]
|
||||
(ReqArg
|
||||
(\arg opt -> return opt { optLines = read arg :: Int })
|
||||
"LINES")
|
||||
"New one-time pad's length (in lines of 48 characters) (for generation)"
|
||||
, Option "V" ["version"]
|
||||
(NoArg
|
||||
(\_ -> do
|
||||
hPutStrLn stderr "Version 0.01"
|
||||
exitWith ExitSuccess))
|
||||
"Print version"
|
||||
, Option "h" ["help"]
|
||||
(NoArg
|
||||
(\_ -> do
|
||||
prg <- getProgName
|
||||
putStrLn "usage: OneTimePad [-h] [-V] [--lines LINES] [-i FILE] [-o FILE] [-p FILE] [--encrypt | --decrypt | --generate]"
|
||||
hPutStrLn stderr (usageInfo prg options)
|
||||
exitWith ExitSuccess))
|
||||
"Show this help message and exit"
|
||||
]
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
args <- getArgs
|
||||
let (actions, nonOptions, errors) = getOpt RequireOrder options args
|
||||
opts <- Prelude.foldl (>>=) (return startOptions) actions
|
||||
let Options { optCommand = command
|
||||
, optInput = input
|
||||
, optOutput = output
|
||||
, optPad = (inPad, outPad)
|
||||
, optLines = linecnt } = opts
|
||||
|
||||
case command of
|
||||
"generate" -> generate linecnt output
|
||||
"encrypt" -> do
|
||||
inputContents <- clean <$> input
|
||||
padContents <- inPad
|
||||
output $ format $ encrypt inputContents $ unformat $ T.concat
|
||||
$ dropWhile (\t -> T.head t == '-' || T.head t == '#')
|
||||
$ T.lines padContents
|
||||
"decrypt" -> do
|
||||
inputContents <- unformat <$> input
|
||||
padContents <- inPad
|
||||
output $ decrypt inputContents $ unformat $ T.concat
|
||||
$ dropWhile (\t -> T.head t == '-' || T.head t == '#')
|
||||
$ T.lines padContents
|
||||
let discardLines = ceiling
|
||||
$ ((/) `on` fromIntegral) (T.length inputContents) 48
|
||||
outPad $ discard discardLines $ T.lines padContents
|
||||
|
||||
{- | Discard used pad lines. Is only called at decryption to enable using the
|
||||
same pad file for both encryption and decryption.
|
||||
-}
|
||||
discard :: Int -> [T.Text] -> T.Text
|
||||
discard 0 ts = T.unlines ts
|
||||
discard x (t:ts) = if (T.head t == '-' || T.head t == '#')
|
||||
then T.unlines [t, (discard x ts)]
|
||||
else T.unlines [(T.append (T.pack "- ") t), (discard (x-1) ts)]
|
||||
|
||||
{- | Clean the text from symbols that cannot be encrypted.
|
||||
-}
|
||||
clean :: T.Text -> T.Text
|
||||
clean = T.map toUpper . T.filter (\c -> let oc = ord c
|
||||
in oc >= 65 && oc <= 122
|
||||
&& (not $ oc >=91 && oc <= 96))
|
||||
|
||||
{- | Format text (usually encrypted text) for pretty-printing it in a similar
|
||||
way to the example from Wikipedia (see Rosetta Code page for this task)
|
||||
-}
|
||||
format :: T.Text -> T.Text
|
||||
format = T.unlines . map (T.intercalate (T.pack " ") . T.chunksOf 6)
|
||||
. T.chunksOf 48
|
||||
|
||||
{- | Unformat encrypted text, getting rid of characters that are irrelevant for
|
||||
decryption.
|
||||
-}
|
||||
unformat :: T.Text -> T.Text
|
||||
unformat = T.filter (\c -> c/='\n' && c/=' ')
|
||||
|
||||
{- | Generate a one-time pad and write it to file (specified as second
|
||||
parameter). Note: this only works on operating systems that have the
|
||||
"/dev/random" file.
|
||||
-}
|
||||
generate :: Int -> (T.Text -> IO ()) -> IO ()
|
||||
generate lines output = do
|
||||
withBinaryFile "/dev/random" ReadMode
|
||||
(\handle -> do
|
||||
contents <- replicateM (48 * lines) $ hGetChar handle
|
||||
time <- getCurrentTime
|
||||
output
|
||||
$ T.unlines [ T.pack
|
||||
$ "# OTP pad, generated by https://github.com/kssytsrk/one-time-pad on "
|
||||
++ show time
|
||||
, format $ T.pack
|
||||
$ map (chr . (65 +) . flip mod 26 . ord) contents
|
||||
])
|
||||
|
||||
-- Helper function for encryption/decryption.
|
||||
crypt :: (Int -> Int -> Int) -> T.Text -> T.Text -> T.Text
|
||||
crypt f = T.zipWith ((chr .) . f `on` ord)
|
||||
|
||||
-- Encrypt first parameter's contents, using the second parameter as a key.
|
||||
encrypt :: T.Text -> T.Text -> T.Text
|
||||
encrypt = crypt ((((+65) . flip mod 26 . subtract 130) .) . (+))
|
||||
|
||||
-- Decrypt first parameter's contents, using the second parameter as a key.
|
||||
decrypt :: T.Text -> T.Text -> T.Text
|
||||
decrypt = crypt ((((+65) . flip mod 26) .) . (-))
|
||||
5
Task/One-time-pad/J/one-time-pad-1.j
Normal file
5
Task/One-time-pad/J/one-time-pad-1.j
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require'convert/misc/vig convert/misc/ascii85'
|
||||
randseq=: {{ 2!:0'dd 2>/dev/null if=/dev/urandom count=1 bs=',":y }}
|
||||
genpad=: {{ EMPTY [ (randseq y) fwrite x }}
|
||||
encrypt=: {{ toascii85 (fread x) 0 vig a. y }}
|
||||
decrypt=: {{ (fread x) 1 vig a. fromascii85 y }}
|
||||
6
Task/One-time-pad/J/one-time-pad-2.j
Normal file
6
Task/One-time-pad/J/one-time-pad-2.j
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
'example' genpad 1000
|
||||
'example' encrypt 'this is a test'
|
||||
hc>*>I2nEB,6b#MdE;~>
|
||||
|
||||
'example' decrypt 'hc>*>I2nEB,6b#MdE;~>'
|
||||
this is a test
|
||||
126
Task/One-time-pad/Java/one-time-pad.java
Normal file
126
Task/One-time-pad/Java/one-time-pad.java
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class OneTimePad {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String controlName = "AtomicBlonde";
|
||||
generatePad(controlName, 5, 60, 65, 90);
|
||||
String text = "IT WAS THE BEST OF TIMES IT WAS THE WORST OF TIMES";
|
||||
String encrypted = parse(true, controlName, text.replaceAll(" ", ""));
|
||||
String decrypted = parse(false, controlName, encrypted);
|
||||
System.out.println("Input text = " + text);
|
||||
System.out.println("Encrypted text = " + encrypted);
|
||||
System.out.println("Decrypted text = " + decrypted);
|
||||
|
||||
controlName = "AtomicBlondeCaseSensitive";
|
||||
generatePad(controlName, 5, 60, 32, 126);
|
||||
text = "It was the best of times, it was the worst of times.";
|
||||
encrypted = parse(true, controlName, text);
|
||||
decrypted = parse(false, controlName, encrypted);
|
||||
System.out.println();
|
||||
System.out.println("Input text = " + text);
|
||||
System.out.println("Encrypted text = " + encrypted);
|
||||
System.out.println("Decrypted text = " + decrypted);
|
||||
}
|
||||
|
||||
private static String parse(boolean encryptText, String controlName, String text) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int minCh = 0;
|
||||
int maxCh = 0;
|
||||
Pattern minChPattern = Pattern.compile("^# MIN_CH = ([\\d]+)$");
|
||||
Pattern maxChPattern = Pattern.compile("^# MAX_CH = ([\\d]+)$");
|
||||
boolean validated = false;
|
||||
try (BufferedReader in = new BufferedReader(new FileReader(getFileName(controlName))); ) {
|
||||
String inLine = null;
|
||||
while ( (inLine = in.readLine()) != null ) {
|
||||
Matcher minMatcher = minChPattern.matcher(inLine);
|
||||
if ( minMatcher.matches() ) {
|
||||
minCh = Integer.parseInt(minMatcher.group(1));
|
||||
continue;
|
||||
}
|
||||
Matcher maxMatcher = maxChPattern.matcher(inLine);
|
||||
if ( maxMatcher.matches() ) {
|
||||
maxCh = Integer.parseInt(maxMatcher.group(1));
|
||||
continue;
|
||||
}
|
||||
if ( ! validated && minCh > 0 && maxCh > 0 ) {
|
||||
validateText(text, minCh, maxCh);
|
||||
validated = true;
|
||||
}
|
||||
// # is comment. - is used key.
|
||||
if ( inLine.startsWith("#") || inLine.startsWith("-") ) {
|
||||
continue;
|
||||
}
|
||||
// Have encryption key.
|
||||
String key = inLine;
|
||||
if ( encryptText ) {
|
||||
for ( int i = 0 ; i < text.length(); i++) {
|
||||
sb.append((char) (((text.charAt(i) - minCh + key.charAt(i) - minCh) % (maxCh - minCh + 1)) + minCh));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( int i = 0 ; i < text.length(); i++) {
|
||||
int decrypt = text.charAt(i) - key.charAt(i);
|
||||
if ( decrypt < 0 ) {
|
||||
decrypt += maxCh - minCh + 1;
|
||||
}
|
||||
decrypt += minCh;
|
||||
sb.append((char) decrypt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void validateText(String text, int minCh, int maxCh) {
|
||||
// Validate text is in range
|
||||
for ( char ch : text.toCharArray() ) {
|
||||
if ( ch != ' ' && (ch < minCh || ch > maxCh) ) {
|
||||
throw new IllegalArgumentException("ERROR 103: Invalid text.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getFileName(String controlName) {
|
||||
return controlName + ".1tp";
|
||||
}
|
||||
|
||||
private static void generatePad(String controlName, int keys, int keyLength, int minCh, int maxCh) {
|
||||
Random random = new Random();
|
||||
try ( BufferedWriter writer = new BufferedWriter(new FileWriter(getFileName(controlName), false)); ) {
|
||||
writer.write("# Lines starting with '#' are ignored.");
|
||||
writer.newLine();
|
||||
writer.write("# Lines starting with '-' are previously used.");
|
||||
writer.newLine();
|
||||
writer.write("# MIN_CH = " + minCh);
|
||||
writer.newLine();
|
||||
writer.write("# MAX_CH = " + maxCh);
|
||||
writer.newLine();
|
||||
for ( int line = 0 ; line < keys ; line++ ) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for ( int ch = 0 ; ch < keyLength ; ch++ ) {
|
||||
sb.append((char) (random.nextInt(maxCh - minCh + 1) + minCh));
|
||||
}
|
||||
writer.write(sb.toString());
|
||||
writer.newLine();
|
||||
}
|
||||
writer.write("# EOF");
|
||||
writer.newLine();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
242
Task/One-time-pad/Kotlin/one-time-pad.kotlin
Normal file
242
Task/One-time-pad/Kotlin/one-time-pad.kotlin
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// version 1.2.31
|
||||
|
||||
import java.io.File
|
||||
import java.security.SecureRandom
|
||||
|
||||
const val CHARS_PER_LINE = 48
|
||||
const val CHUNK_SIZE = 6
|
||||
const val COLS = 8
|
||||
const val DEMO = true // would normally be set to false
|
||||
|
||||
enum class FileType { OTP, ENC, DEC }
|
||||
|
||||
fun Char.isAlpha() = this in 'A'..'Z'
|
||||
|
||||
fun String.toAlpha() = this.filter { it.isAlpha() }
|
||||
|
||||
fun String.isOtpRelated() = endsWith(".1tp") || endsWith(".1tp_cpy") ||
|
||||
endsWith(".1tp_enc") || endsWith(".1tp_dec")
|
||||
|
||||
fun makePad(nLines: Int): String {
|
||||
val nChars = nLines * CHARS_PER_LINE
|
||||
val sr = SecureRandom()
|
||||
val sb = StringBuilder(nChars)
|
||||
/* generate random upper case letters */
|
||||
for (i in 0 until nChars) sb.append((sr.nextInt(26) + 65).toChar())
|
||||
return sb.toString().inChunks(nLines, FileType.OTP)
|
||||
}
|
||||
|
||||
fun vigenere(text: String, key: String, encrypt: Boolean = true): String {
|
||||
val sb = StringBuilder(text.length)
|
||||
for ((i, c) in text.withIndex()) {
|
||||
val ci = if (encrypt)
|
||||
(c.toInt() + key[i].toInt() - 130) % 26
|
||||
else
|
||||
(c.toInt() - key[i].toInt() + 26) % 26
|
||||
sb.append((ci + 65).toChar())
|
||||
}
|
||||
val temp = sb.length % CHARS_PER_LINE
|
||||
if (temp > 0) { // pad with random characters so each line is a full one
|
||||
val sr = SecureRandom()
|
||||
for (i in temp until CHARS_PER_LINE) sb.append((sr.nextInt(26) + 65).toChar())
|
||||
}
|
||||
val ft = if (encrypt) FileType.ENC else FileType.DEC
|
||||
return sb.toString().inChunks(sb.length / CHARS_PER_LINE, ft)
|
||||
}
|
||||
|
||||
fun String.inChunks(nLines: Int, ft: FileType): String {
|
||||
val chunks = this.chunked(CHUNK_SIZE)
|
||||
val sb = StringBuilder(this.length + nLines * (COLS + 1))
|
||||
for (i in 0 until nLines) {
|
||||
val j = i * COLS
|
||||
sb.append(" ${chunks.subList(j, j + COLS).joinToString(" ")}\n")
|
||||
}
|
||||
val s = " file\n" + sb.toString()
|
||||
return when (ft) {
|
||||
FileType.OTP -> "# OTP" + s
|
||||
FileType.ENC -> "# Encrypted" + s
|
||||
FileType.DEC -> "# Decrypted" + s
|
||||
}
|
||||
}
|
||||
|
||||
fun menu(): Int {
|
||||
println("""
|
||||
|
|
||||
|1. Create one time pad file.
|
||||
|
|
||||
|2. Delete one time pad file.
|
||||
|
|
||||
|3. List one time pad files.
|
||||
|
|
||||
|4. Encrypt plain text.
|
||||
|
|
||||
|5. Decrypt cipher text.
|
||||
|
|
||||
|6. Quit program.
|
||||
|
|
||||
""".trimMargin())
|
||||
var choice: Int?
|
||||
do {
|
||||
print("Your choice (1 to 6) : ")
|
||||
choice = readLine()!!.toIntOrNull()
|
||||
}
|
||||
while (choice == null || choice !in 1..6)
|
||||
return choice
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
mainLoop@ while (true) {
|
||||
val choice = menu()
|
||||
println()
|
||||
when (choice) {
|
||||
1 -> { // Create OTP
|
||||
println("Note that encrypted lines always contain 48 characters.\n")
|
||||
print("OTP file name to create (without extension) : ")
|
||||
val fileName = readLine()!! + ".1tp"
|
||||
var nLines: Int?
|
||||
|
||||
do {
|
||||
print("Number of lines in OTP (max 1000) : ")
|
||||
nLines = readLine()!!.toIntOrNull()
|
||||
}
|
||||
while (nLines == null || nLines !in 1..1000)
|
||||
|
||||
val key = makePad(nLines)
|
||||
File(fileName).writeText(key)
|
||||
println("\n'$fileName' has been created in the current directory.")
|
||||
if (DEMO) {
|
||||
// a copy of the OTP file would normally be on a different machine
|
||||
val fileName2 = fileName + "_cpy" // copy for decryption
|
||||
File(fileName2).writeText(key)
|
||||
println("'$fileName2' has been created in the current directory.")
|
||||
println("\nThe contents of these files are :\n")
|
||||
println(key)
|
||||
}
|
||||
}
|
||||
|
||||
2 -> { // Delete OTP
|
||||
println("Note that this will also delete ALL associated files.\n")
|
||||
print("OTP file name to delete (without extension) : ")
|
||||
val toDelete1 = readLine()!! + ".1tp"
|
||||
val toDelete2 = toDelete1 + "_cpy"
|
||||
val toDelete3 = toDelete1 + "_enc"
|
||||
val toDelete4 = toDelete1 + "_dec"
|
||||
val allToDelete = listOf(toDelete1, toDelete2, toDelete3, toDelete4)
|
||||
var deleted = 0
|
||||
println()
|
||||
for (name in allToDelete) {
|
||||
val f = File(name)
|
||||
if (f.exists()) {
|
||||
f.delete()
|
||||
deleted++
|
||||
println("'$name' has been deleted from the current directory.")
|
||||
}
|
||||
}
|
||||
if (deleted == 0) println("There are no files to delete.")
|
||||
}
|
||||
|
||||
3 -> { // List OTPs
|
||||
println("The OTP (and related) files in the current directory are:\n")
|
||||
val otpFiles = File(".").listFiles().filter {
|
||||
it.isFile() && it.name.isOtpRelated()
|
||||
}.map { it.name }.toMutableList()
|
||||
otpFiles.sort()
|
||||
println(otpFiles.joinToString("\n"))
|
||||
}
|
||||
|
||||
4 -> { // Encrypt
|
||||
print("OTP file name to use (without extension) : ")
|
||||
val keyFile = readLine()!! + ".1tp"
|
||||
val kf = File(keyFile)
|
||||
if (kf.exists()) {
|
||||
val lines = File(keyFile).readLines().toMutableList()
|
||||
var first = lines.size
|
||||
for (i in 0 until lines.size) {
|
||||
if (lines[i].startsWith(" ")) {
|
||||
first = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (first == lines.size) {
|
||||
println("\nThat file has no unused lines.")
|
||||
continue@mainLoop
|
||||
}
|
||||
val lines2 = lines.drop(first) // get rid of comments and used lines
|
||||
|
||||
println("Text to encrypt :-\n")
|
||||
val text = readLine()!!.toUpperCase().toAlpha()
|
||||
val len = text.length
|
||||
var nLines = len / CHARS_PER_LINE
|
||||
if (len % CHARS_PER_LINE > 0) nLines++
|
||||
|
||||
if (lines2.size >= nLines) {
|
||||
val key = lines2.take(nLines).joinToString("").toAlpha()
|
||||
val encrypted = vigenere(text, key)
|
||||
val encFile = keyFile + "_enc"
|
||||
File(encFile).writeText(encrypted)
|
||||
println("\n'$encFile' has been created in the current directory.")
|
||||
for (i in first until first + nLines) {
|
||||
lines[i] = "-" + lines[i].drop(1)
|
||||
}
|
||||
File(keyFile).writeText(lines.joinToString("\n"))
|
||||
if (DEMO) {
|
||||
println("\nThe contents of the encrypted file are :\n")
|
||||
println(encrypted)
|
||||
}
|
||||
}
|
||||
else println("Not enough lines left in that file to do encryption")
|
||||
}
|
||||
else println("\nThat file does not exist.")
|
||||
}
|
||||
|
||||
5 -> { // Decrypt
|
||||
print("OTP file name to use (without extension) : ")
|
||||
val keyFile = readLine()!! + ".1tp_cpy"
|
||||
val kf = File(keyFile)
|
||||
if (kf.exists()) {
|
||||
val keyLines = File(keyFile).readLines().toMutableList()
|
||||
var first = keyLines.size
|
||||
for (i in 0 until keyLines.size) {
|
||||
if (keyLines[i].startsWith(" ")) {
|
||||
first = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (first == keyLines.size) {
|
||||
println("\nThat file has no unused lines.")
|
||||
continue@mainLoop
|
||||
}
|
||||
val keyLines2 = keyLines.drop(first) // get rid of comments and used lines
|
||||
|
||||
val encFile = keyFile.dropLast(3) + "enc"
|
||||
val ef = File(encFile)
|
||||
if (ef.exists()) {
|
||||
val encLines = File(encFile).readLines().drop(1) // exclude comment line
|
||||
val nLines = encLines.size
|
||||
if (keyLines2.size >= nLines) {
|
||||
val encrypted = encLines.joinToString("").toAlpha()
|
||||
val key = keyLines2.take(nLines).joinToString("").toAlpha()
|
||||
val decrypted = vigenere(encrypted, key, false)
|
||||
val decFile = keyFile.dropLast(3) + "dec"
|
||||
File(decFile).writeText(decrypted)
|
||||
println("\n'$decFile' has been created in the current directory.")
|
||||
for (i in first until first + nLines) {
|
||||
keyLines[i] = "-" + keyLines[i].drop(1)
|
||||
}
|
||||
File(keyFile).writeText(keyLines.joinToString("\n"))
|
||||
if (DEMO) {
|
||||
println("\nThe contents of the decrypted file are :\n")
|
||||
println(decrypted)
|
||||
}
|
||||
}
|
||||
else println("Not enough lines left in that file to do decryption")
|
||||
}
|
||||
else println("\n'$encFile' is missing.")
|
||||
}
|
||||
else println("\nThat file does not exist.")
|
||||
}
|
||||
|
||||
else -> return // Quit
|
||||
}
|
||||
}
|
||||
}
|
||||
229
Task/One-time-pad/Nim/one-time-pad.nim
Normal file
229
Task/One-time-pad/Nim/one-time-pad.nim
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
import os, re, sequtils, strformat, strutils
|
||||
import nimcrypto
|
||||
|
||||
# One-time pad file signature.
|
||||
const Magic = "#one-time pad"
|
||||
|
||||
# Suffix for pad files.
|
||||
const Suffix = ".1tp"
|
||||
|
||||
proc log(msg: string) =
|
||||
## Log a message.
|
||||
stderr.write msg
|
||||
stderr.write '\n'
|
||||
|
||||
proc makeKeys(n, size: Positive): seq[string] =
|
||||
## Generate "n" secure, random keys of "size" bytes.
|
||||
|
||||
# We're generating and storing keys in their hexadecimal form to make
|
||||
# one-time pad files a little more human readable and to ensure a key
|
||||
# can not start with a hyphen.
|
||||
var bytes = newSeq[byte](size)
|
||||
for _ in 1..n:
|
||||
if randomBytes(bytes) != size:
|
||||
raise newException(ValueError, "unable to build keys.")
|
||||
result.add bytes.mapIt(it.toHex).join()
|
||||
|
||||
proc makePad(name: string; padSize, keySize: Positive): string =
|
||||
## Create a new one-time pad identified by the given name.
|
||||
## Args:
|
||||
## name: unique one-time pad identifier.
|
||||
## padSize: the number of keys (or pages) in the pad.
|
||||
## keySize: the number of bytes per key.
|
||||
## Returns:
|
||||
## the new one-time pad as a string.
|
||||
|
||||
let pad = @[Magic, &"#name={name}", &"#size={padSize}"] & makeKeys(padSize, keySize)
|
||||
result = pad.join("\n")
|
||||
|
||||
proc `xor`(message, key: string): string =
|
||||
## Return "message" XOR-ed with "key".
|
||||
##
|
||||
## Args:
|
||||
## message: plaintext or cyphertext to be encrypted or decrypted.
|
||||
## key: encryption and decryption key.
|
||||
## Returns:
|
||||
## plaintext or cyphertext as a string.
|
||||
|
||||
if key.len < message.len:
|
||||
quit("Key size is too short to encrypt/decrypt the message.", QuitFailure)
|
||||
result = newStringOfCap(message.len)
|
||||
var keyIndex = 0
|
||||
for msgChar in message:
|
||||
result.add chr(ord(msgChar) xor ord(key[keyIndex]))
|
||||
inc keyIndex
|
||||
|
||||
proc useKey(pad: var string): string =
|
||||
## Use the next available key from the given one-time pad.
|
||||
##
|
||||
## Args:
|
||||
## pad: a one-time pad, updated.
|
||||
## Returns:
|
||||
## the key.
|
||||
|
||||
var matches: array[1, string]
|
||||
let pos = pad.find(re"(?m)(^[A-F0-9]+$)", matches)
|
||||
if pos < 0:
|
||||
quit("Pad is all used up.", QuitFailure)
|
||||
|
||||
pad.insert("-", pos)
|
||||
result = matches[0]
|
||||
|
||||
proc writePad(path: string; padSize, keySize: Positive) =
|
||||
## Write a new one-time pad to the given path.
|
||||
##
|
||||
## Args:
|
||||
## path: path to write one-time pad to.
|
||||
## padSize: the number of keys (or pages) in the pad.
|
||||
## keySize: the number of bytes per key.
|
||||
|
||||
if fileExists(path):
|
||||
quit("Pad " & path & " already exists", QuitFailure)
|
||||
try:
|
||||
path.writeFile(makePad(path.extractFilename(), padSize, keySize))
|
||||
except IOError:
|
||||
quit("Unable to write file " & path, QuitFailure)
|
||||
log("New one-time pad written to " & path)
|
||||
|
||||
proc process(pad, message: string; outfile: File) =
|
||||
## Encrypt or decrypt "message" using the given pad.
|
||||
##
|
||||
## Args:
|
||||
## pad: path to one-time pad.
|
||||
## message: plaintext or ciphertext message to encrypt or decrypt.
|
||||
## outfile: file-like object to write to.
|
||||
|
||||
if not fileExists(pad):
|
||||
quit("No such pad: " & pad, QuitFailure)
|
||||
let start = pad.readLines(1)
|
||||
if start.len == 0 or start[0] != Magic:
|
||||
quit(&"file '{pad}' does not look like a one-time pad", QuitFailure)
|
||||
|
||||
# Rewrites the entire one-time pad every time
|
||||
var padData = pad.readFile()
|
||||
let key = padData.useKey().parseHexStr()
|
||||
pad.writeFile(padData)
|
||||
|
||||
outfile.write(message xor key)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
|
||||
import parseopt
|
||||
|
||||
proc printUsage() =
|
||||
echo "Usage: ", getAppFilename().lastPathPart,
|
||||
" [-h] [--length LENGTH] [--key-size KEY_SIZE] [-o OUTFILE]"
|
||||
echo " [--encrypt FILE | --decrypt FILE] pad"
|
||||
echo ""
|
||||
echo """One-time pad.
|
||||
|
||||
positional arguments:
|
||||
pad Path to one-time pad. If neither --encrypt or --decrypt
|
||||
are given, will create a new pad.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--length LENGTH Pad size. Ignored if --encrypt or --decrypt are given.
|
||||
Defaults to 10.
|
||||
--key-size KEY_SIZE Key size in bytes. Ignored if --encrypt or --decrypt
|
||||
are given. Defaults to 64.
|
||||
--outfile OUTFILE Write encoded/decoded message to a file. Ignored if
|
||||
--encrypt or --decrypt is not given. Defaults to
|
||||
stdout.
|
||||
--encrypt FILE Encrypt FILE using the next available key from pad.
|
||||
--decrypt FILE Decrypt FILE using the next available key from pad.
|
||||
"""
|
||||
|
||||
var
|
||||
parser = initOptParser(shortNoVal = {'h'}, longNoval = @["help"])
|
||||
padPath: string
|
||||
length = 10
|
||||
outpath = ""
|
||||
encryptPath = ""
|
||||
decryptPath = ""
|
||||
keySize = 64
|
||||
encrypt = false
|
||||
decrypt = false
|
||||
|
||||
for kind, key, val in parser.getopt():
|
||||
case kind
|
||||
|
||||
of cmdShortOption:
|
||||
printUsage()
|
||||
if key != "h":
|
||||
quit("Wrong option: " & key, QuitFailure)
|
||||
elif val.len != 0:
|
||||
quit("Wrong value for option -h", QuitFailure)
|
||||
else:
|
||||
quit(QuitSuccess)
|
||||
|
||||
of cmdLongOption:
|
||||
case key
|
||||
|
||||
of "help":
|
||||
printUsage()
|
||||
quit(QuitSuccess)
|
||||
|
||||
of "length":
|
||||
try:
|
||||
length = parseInt(val)
|
||||
if length < 2: raise newException(ValueError, "")
|
||||
except ValueError:
|
||||
quit("Wrong length: " & val, QuitFailure)
|
||||
|
||||
of "encrypt":
|
||||
encryptPath = val
|
||||
encrypt = true
|
||||
|
||||
of "decrypt":
|
||||
decryptPath = val
|
||||
decrypt = true
|
||||
|
||||
of "outfile":
|
||||
outPath = val
|
||||
|
||||
of "key-size":
|
||||
try:
|
||||
keySize = parseInt(val)
|
||||
if length < 2: raise newException(ValueError, "")
|
||||
except ValueError:
|
||||
quit("Wrong key size: " & val, QuitFailure)
|
||||
|
||||
else:
|
||||
quit("Invalid option: " & key, QuitFailure)
|
||||
|
||||
of cmdArgument:
|
||||
padPath = if key.endsWith(Suffix): key else: key & Suffix
|
||||
|
||||
of cmdEnd:
|
||||
discard # Cannot not occur.
|
||||
|
||||
if padPath.len == 0:
|
||||
quit("Missing pad file.", QuitFailure)
|
||||
|
||||
if encrypt and decrypt:
|
||||
quit("Incompatible options: encrypt and decrypt", QuitFailure)
|
||||
|
||||
if encrypt or decrypt:
|
||||
|
||||
var outfile: File
|
||||
if outpath.len == 0:
|
||||
outfile = stdout
|
||||
else:
|
||||
try:
|
||||
outfile = outpath.open(fmWrite)
|
||||
except IOError:
|
||||
quit("Unable to open output file.", QuitFailure)
|
||||
|
||||
let message = try:
|
||||
if encrypt: encryptPath.readFile()
|
||||
else: decryptPath.readFile()
|
||||
except IOError:
|
||||
quit("Unable to open file to encrypt or decrypt.", QuitFailure)
|
||||
|
||||
padPath.process(message, outfile)
|
||||
if outfile != stdout: outfile.close()
|
||||
|
||||
else:
|
||||
padPath.writePad(length, keySize)
|
||||
17
Task/One-time-pad/Perl/one-time-pad.pl
Normal file
17
Task/One-time-pad/Perl/one-time-pad.pl
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 20200814 added Perl programming solution
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Crypt::OTP;
|
||||
use Bytes::Random::Secure qw( random_bytes );
|
||||
|
||||
print "Message : ", my $message = "show me the monKey", "\n";
|
||||
|
||||
my $otp = random_bytes(length $message);
|
||||
print "Ord(OTP) : ", ( map { ord($_).' ' } (split //, $otp) ) , "\n";
|
||||
|
||||
my $cipher = OTP( $otp, $message, 1 );
|
||||
print "Ord(Cipher) : ", ( map { ord($_).' ' } (split //, $cipher) ) , "\n";
|
||||
|
||||
print "Decoded : ", OTP( $otp, $cipher, 1 ), "\n";
|
||||
197
Task/One-time-pad/Python/one-time-pad.py
Normal file
197
Task/One-time-pad/Python/one-time-pad.py
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
"""One-time pad using an XOR cipher. Requires Python >=3.6."""
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
import pathlib
|
||||
import re
|
||||
import secrets
|
||||
import sys
|
||||
|
||||
|
||||
# One-time pad file signature.
|
||||
MAGIC = "#one-time pad"
|
||||
|
||||
|
||||
def make_keys(n, size):
|
||||
"""Generate ``n`` secure, random keys of ``size`` bytes."""
|
||||
# We're generating and storing keys in their hexadecimal form to make
|
||||
# one-time pad files a little more human readable and to ensure a key
|
||||
# can not start with a hyphen.
|
||||
return (secrets.token_hex(size) for _ in range(n))
|
||||
|
||||
|
||||
def make_pad(name, pad_size, key_size):
|
||||
"""Create a new one-time pad identified by the given name.
|
||||
|
||||
Args:
|
||||
name (str): Unique one-time pad identifier.
|
||||
pad_size (int): The number of keys (or pages) in the pad.
|
||||
key_size (int): The number of bytes per key.
|
||||
Returns:
|
||||
The new one-time pad as a string.
|
||||
"""
|
||||
pad = [
|
||||
MAGIC,
|
||||
f"#name={name}",
|
||||
f"#size={pad_size}",
|
||||
*make_keys(pad_size, key_size),
|
||||
]
|
||||
|
||||
return "\n".join(pad)
|
||||
|
||||
|
||||
def xor(message, key):
|
||||
"""Return ``message`` XOR-ed with ``key``.
|
||||
|
||||
Args:
|
||||
message (bytes): Plaintext or cyphertext to be encrypted or decrypted.
|
||||
key (bytes): Encryption and decryption key.
|
||||
Returns:
|
||||
Plaintext or cyphertext as a byte string.
|
||||
"""
|
||||
return bytes(mc ^ kc for mc, kc in zip(message, itertools.cycle(key)))
|
||||
|
||||
|
||||
def use_key(pad):
|
||||
"""Use the next available key from the given one-time pad.
|
||||
|
||||
Args:
|
||||
pad (str): A one-time pad.
|
||||
Returns:
|
||||
(str, str) A two-tuple of updated pad and key.
|
||||
"""
|
||||
match = re.search(r"^[a-f0-9]+$", pad, re.MULTILINE)
|
||||
if not match:
|
||||
error("pad is all used up")
|
||||
|
||||
key = match.group()
|
||||
pos = match.start()
|
||||
|
||||
return (f"{pad[:pos]}-{pad[pos:]}", key)
|
||||
|
||||
|
||||
def log(msg):
|
||||
"""Log a message."""
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write("\n")
|
||||
|
||||
|
||||
def error(msg):
|
||||
"""Exit with an error message."""
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write("\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def write_pad(path, pad_size, key_size):
|
||||
"""Write a new one-time pad to the given path.
|
||||
|
||||
Args:
|
||||
path (pathlib.Path): Path to write one-time pad to.
|
||||
length (int): Number of keys in the pad.
|
||||
"""
|
||||
if path.exists():
|
||||
error(f"pad '{path}' already exists")
|
||||
|
||||
with path.open("w") as fd:
|
||||
fd.write(make_pad(path.name, pad_size, key_size))
|
||||
|
||||
log(f"New one-time pad written to {path}")
|
||||
|
||||
|
||||
def main(pad, message, outfile):
|
||||
"""Encrypt or decrypt ``message`` using the given pad.
|
||||
|
||||
Args:
|
||||
pad (pathlib.Path): Path to one-time pad.
|
||||
message (bytes): Plaintext or ciphertext message to encrypt or decrypt.
|
||||
outfile: File-like object to write to.
|
||||
"""
|
||||
if not pad.exists():
|
||||
error(f"no such pad '{pad}'")
|
||||
|
||||
with pad.open("r") as fd:
|
||||
if fd.readline().strip() != MAGIC:
|
||||
error(f"file '{pad}' does not look like a one-time pad")
|
||||
|
||||
# Rewrites the entire one-time pad every time
|
||||
with pad.open("r+") as fd:
|
||||
updated, key = use_key(fd.read())
|
||||
|
||||
fd.seek(0)
|
||||
fd.write(updated)
|
||||
|
||||
outfile.write(xor(message, bytes.fromhex(key)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Command line interface
|
||||
parser = argparse.ArgumentParser(description="One-time pad.")
|
||||
|
||||
parser.add_argument(
|
||||
"pad",
|
||||
help=(
|
||||
"Path to one-time pad. If neither --encrypt or --decrypt "
|
||||
"are given, will create a new pad."
|
||||
),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--length",
|
||||
type=int,
|
||||
default=10,
|
||||
help="Pad size. Ignored if --encrypt or --decrypt are given. Defaults to 10.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--key-size",
|
||||
type=int,
|
||||
default=64,
|
||||
help="Key size in bytes. Ignored if --encrypt or --decrypt are given. Defaults to 64.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--outfile",
|
||||
type=argparse.FileType("wb"),
|
||||
default=sys.stdout.buffer,
|
||||
help=(
|
||||
"Write encoded/decoded message to a file. Ignored if --encrypt or "
|
||||
"--decrypt is not given. Defaults to stdout."
|
||||
),
|
||||
)
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
|
||||
group.add_argument(
|
||||
"--encrypt",
|
||||
metavar="FILE",
|
||||
type=argparse.FileType("rb"),
|
||||
help="Encrypt FILE using the next available key from pad.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--decrypt",
|
||||
metavar="FILE",
|
||||
type=argparse.FileType("rb"),
|
||||
help="Decrypt FILE using the next available key from pad.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.encrypt:
|
||||
message = args.encrypt.read()
|
||||
elif args.decrypt:
|
||||
message = args.decrypt.read()
|
||||
else:
|
||||
message = None
|
||||
|
||||
# Sometimes necessary if message came from stdin
|
||||
if isinstance(message, str):
|
||||
message = message.encode()
|
||||
|
||||
pad = pathlib.Path(args.pad).with_suffix(".1tp")
|
||||
|
||||
if message:
|
||||
main(pad, message, args.outfile)
|
||||
else:
|
||||
write_pad(pad, args.length, args.key_size)
|
||||
29
Task/One-time-pad/Raku/one-time-pad-1.raku
Normal file
29
Task/One-time-pad/Raku/one-time-pad-1.raku
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
sub MAIN {
|
||||
put "Generate data for one time pad encryption.\n" ~
|
||||
"File will have .1tp extension.";
|
||||
my $fn;
|
||||
loop {
|
||||
$fn = prompt 'Filename for one time pad data: ';
|
||||
if $fn !~~ /'.1tp' $/ { $fn ~= '.1tp' }
|
||||
if $fn.IO.e {
|
||||
my $ow = prompt "$fn aready exists, over-write? y/[n] ";
|
||||
last if $ow ~~ m:i/'y'/;
|
||||
redo;
|
||||
}
|
||||
last;
|
||||
}
|
||||
|
||||
put 'Each line will contain 48 characters of encyption data.';
|
||||
my $lines = prompt 'How many lines of data to generate? [1000] ';
|
||||
$lines ||= 1000;
|
||||
generate($fn, $lines);
|
||||
say "One-time-pad data saved to: ", $fn.IO.absolute;
|
||||
|
||||
sub generate ( $fn, $lines) {
|
||||
use Crypt::Random;
|
||||
$fn.IO.spurt: "# one-time-pad encryption data\n" ~
|
||||
((sprintf(" %s %s %s %s %s %s %s %s\n",
|
||||
((('A'..'Z')[crypt_random_uniform(26)] xx 6).join) xx 8))
|
||||
xx $lines).join;
|
||||
}
|
||||
}
|
||||
52
Task/One-time-pad/Raku/one-time-pad-2.raku
Normal file
52
Task/One-time-pad/Raku/one-time-pad-2.raku
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
sub s2v ($s) { $s.uc.comb(/ <[ A..Z ]> /)».ord »-» 65 }
|
||||
sub v2s (@v) { (@v »%» 26 »+» 65)».chr.join }
|
||||
|
||||
sub hide ($secret, $otp) { v2s(s2v($secret) »+» s2v($otp)) }
|
||||
sub reveal ($hidden, $otp) { v2s(s2v($hidden) »-» s2v($otp)) }
|
||||
|
||||
sub otp-data ($fn, $lines) {
|
||||
my $fh = $fn.IO.open :rw;
|
||||
my $data;
|
||||
my $count = 0;
|
||||
repeat {
|
||||
my $pos = $fh.tell;
|
||||
my $line = $fh.get;
|
||||
if $line.substr(0,1) ne '-'|'#' {
|
||||
$data ~= $line;
|
||||
$fh.seek($pos);
|
||||
$fh.put: '-' ~ $line.substr(1);
|
||||
$count++;
|
||||
}
|
||||
} until $count == $lines or $fh.eof;
|
||||
note "Insufficient lines of data remaining in $fn" if $count != $lines;
|
||||
$data;
|
||||
}
|
||||
|
||||
sub otp-size (Str $string) { ceiling $string.uc.comb(/ <[ A..Z ]> /) / 48 }
|
||||
|
||||
sub otp-encrypt ( $secret, $fn ) {
|
||||
my $otp-size = otp-size $secret;
|
||||
my $otp-data = otp-data($fn, $otp-size);
|
||||
my $encrypted = hide $secret, $otp-data;
|
||||
# pad encryted text out to a full line with random text
|
||||
$encrypted ~= ('A'..'Z').roll while $encrypted.chars % 48;
|
||||
join "\n", $encrypted.comb(6).rotor(8, :partial).map:
|
||||
{ sprintf "{ join ' ', "%s" xx $_ }", $_ };
|
||||
}
|
||||
|
||||
sub otp-decrypt ( $secret, $fn ) {
|
||||
my $otp-size = otp-size $secret;
|
||||
my $otp-data = otp-data($fn, $otp-size);
|
||||
my $plain-text = reveal $secret, $otp-data;
|
||||
join "\n", $plain-text.comb(6).rotor(8, :partial).map:
|
||||
{ sprintf "{ join ' ', "%s" xx $_ }", $_ };
|
||||
}
|
||||
|
||||
my $otp-encrypt-fn = 'rosettacode.1tp';
|
||||
my $otp-decrypt-fn = 'rosettacopy.1tp';
|
||||
|
||||
my $secret = "Beware the Jabberwock, my son! The jaws that bite, the claws that catch!";
|
||||
|
||||
say "Secret:\n$secret\n\nEncrypted:";
|
||||
say my $encrypted = otp-encrypt $secret, $otp-encrypt-fn;
|
||||
say "\nDecrypted:\n", otp-decrypt $encrypted, $otp-decrypt-fn;
|
||||
46
Task/One-time-pad/Tcl/one-time-pad.tcl
Normal file
46
Task/One-time-pad/Tcl/one-time-pad.tcl
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
puts "# True random chars for one-time pad"
|
||||
|
||||
proc randInt { min max } {
|
||||
set randDev [open /dev/urandom rb]
|
||||
set random [read $randDev 8]
|
||||
binary scan $random H16 random
|
||||
set random [expr {([scan $random %x] % (($max-$min) + 1) + $min)}]
|
||||
close $randDev
|
||||
return $random
|
||||
}
|
||||
|
||||
proc randStr { sLen grp alfa } {
|
||||
set aLen [string length $alfa]; incr aLen -1
|
||||
set rs ""
|
||||
for {set i 0} {$i < $sLen} {incr i} {
|
||||
if { [expr {$i % $grp} ] == 0} { append rs " " }
|
||||
set r [randInt 0 $aLen]
|
||||
set char [string index $alfa $r]
|
||||
append rs $char
|
||||
##puts "$i: $r $char"
|
||||
}
|
||||
return $rs
|
||||
}
|
||||
|
||||
set alfa "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
set len 48
|
||||
set lines 4
|
||||
set fn "test.1tp"
|
||||
|
||||
# Write file:
|
||||
set fh [open $fn w]
|
||||
puts $fh "# OTP"
|
||||
for {set ln 0} {$ln < $lines} {incr ln} {
|
||||
set line [randStr $len 6 $alfa]
|
||||
##puts "$ln :$line."
|
||||
puts $fh $line
|
||||
}
|
||||
close $fh
|
||||
|
||||
# Read file:
|
||||
puts "# File $fn:"
|
||||
set fh [open $fn]
|
||||
puts [read $fh [file size $fn]]
|
||||
close $fh
|
||||
|
||||
puts "# Done."
|
||||
309
Task/One-time-pad/TypeScript/one-time-pad.ts
Normal file
309
Task/One-time-pad/TypeScript/one-time-pad.ts
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
#!/usr/bin/env node
|
||||
import { writeFileSync, existsSync, readFileSync, unlinkSync } from 'node:fs';
|
||||
//https://www.elitizon.com/2021/01-09-how-to-create-a-cli-command-with-typescript/#:~:text=%20Boost%20your%20productivity%20by%20creating%20your%20own,information.%20Required%3A%20string.%20%20...%20%20More%20
|
||||
|
||||
const a:string[] = process.argv;
|
||||
const argv:string[] = a.splice(2)
|
||||
/**
|
||||
* Extension of the pad files
|
||||
*/
|
||||
const padExtension:string = ".1tp";
|
||||
/**
|
||||
* Extension of the key generated
|
||||
*/
|
||||
const keyExtension:string = ".key";
|
||||
/**
|
||||
* Array of commands usable
|
||||
*/
|
||||
const commands:string[] = ["--generate", "--encrypt","--decrypt"];
|
||||
/**
|
||||
* The alphabet
|
||||
*/
|
||||
const stringLetter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
const main = ():void => {
|
||||
// if no args have been give display help
|
||||
if(argv.length == 0){
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there is a command in the file
|
||||
if(commands.includes(argv[0])){
|
||||
let choise = argv[0].trim().toLowerCase();
|
||||
if(choise === "--generate"){
|
||||
console.log("Generating action chosen");
|
||||
|
||||
// Check if the arguments have been given to generate a new key
|
||||
if(!argv[1]){
|
||||
("Pad name not specified")
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the length of the filename is at least 4 characters
|
||||
if(argv[1].length <= 3){
|
||||
console.log("The name of the pad should be at least 4 letters long");
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the filesize is a number(should be the second argv)
|
||||
let fileSize = +argv[2];
|
||||
if(isNaN(fileSize)){
|
||||
console.log("File size should be a number");
|
||||
help();
|
||||
return
|
||||
}
|
||||
|
||||
createFile(argv[1], fileSize)
|
||||
}
|
||||
|
||||
// If the selected action is to encrypt
|
||||
if( choise === "--encrypt"){
|
||||
console.log("Encrypting action chosen");
|
||||
let padDataName = argv[2] ? `${argv[2].trim()}${padExtension}` : `${argv[1].trim()}${padExtension}`
|
||||
|
||||
// Check if the files exist
|
||||
if(!argv[1] && !argv[2]){
|
||||
console.log("Parameters not given");
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the text file exists and the pad file
|
||||
if(!existsSync(`${argv[1].trim().toLowerCase()}.txt`)){
|
||||
console.log("Text file doesn't exit");
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Reading file")
|
||||
// Get the data from the file as well as the data from the padfile
|
||||
let fileDataEnc = readFileSync(`${argv[1].trim().toLowerCase()}.txt`, 'utf-8');
|
||||
|
||||
if(!existsSync(`${argv[2]}${padExtension}`)){
|
||||
console.log("1tp file doesn't exist, generating new 1pt file");
|
||||
createFile(argv[1], fileDataEnc.length);
|
||||
}
|
||||
|
||||
console.log("Reading 1pt file");
|
||||
// Get the pad data
|
||||
let padDataEnc = RetrievePadData(padDataName);
|
||||
|
||||
// if the file 1pt file has been specified but it's not big enough
|
||||
if(fileDataEnc.length > padDataEnc.length){
|
||||
console.log("The file is bigger than the 1tp file. Generating a new padfile");
|
||||
createFile(argv[1], fileDataEnc.length);
|
||||
padDataEnc = RetrievePadData(`${argv[1]}${padExtension}`)
|
||||
}
|
||||
|
||||
console.log("Converting the file");
|
||||
let keyEnc = padFunction(fileDataEnc,padDataEnc);
|
||||
createFile(`${argv[1]}.key`, keyEnc.length, false, keyEnc);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(choise === "--decrypt"){
|
||||
console.log("Decrypting action chosen");
|
||||
|
||||
// Check if the necessary args were given
|
||||
if(!argv[1]){
|
||||
console.log("No file specified For decryption specified")
|
||||
return;
|
||||
}
|
||||
if(!argv[2]){
|
||||
console.log("No 1pt file was given, Please specify the file to use");
|
||||
return;
|
||||
}
|
||||
if(!argv[3] || argv[3].trim() != '-o') {
|
||||
console.log("No file for output specified");
|
||||
}
|
||||
|
||||
|
||||
let fileNameDec = `${argv[1].trim()}${keyExtension}`;
|
||||
let padNameDec = `${argv[2].trim()}${padExtension}`;
|
||||
|
||||
// Check if the files exists or not
|
||||
if(!existsSync(fileNameDec)){
|
||||
console.log("File specified does not exist");
|
||||
return;
|
||||
}
|
||||
if(!existsSync(padNameDec)){
|
||||
console.log("1pt file specified doesn't exist");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("Read the encrypted file");
|
||||
let fileDataDec = readFileSync(fileNameDec, 'utf-8');
|
||||
console.log("Reading the 1pt file");
|
||||
let padDataDec = RetrievePadData(padNameDec);
|
||||
|
||||
// decrypt the file
|
||||
console.log("Generating the file");
|
||||
let keyDec = padFunction(fileDataDec, padDataDec)
|
||||
|
||||
|
||||
if(argv[3] && argv[3].trim()==='-o'){
|
||||
let textFileDec = `${argv[1].trim()}.txt`;
|
||||
if(!argv[4]){
|
||||
console.log("No output file specified, creating a new file")
|
||||
}
|
||||
if(argv[4] && argv[4].trim().length < 3){
|
||||
console.log("The name of the output file should be greater than 4 characters");
|
||||
console.log(`Creating new file`)
|
||||
}else {
|
||||
argv[4] ? textFileDec = `${argv[4].trim()}.txt` : textFileDec = `${argv[1].trim()}.txt`
|
||||
}
|
||||
|
||||
console.log("Generating text file");
|
||||
createFile(textFileDec, 0, false, keyDec );
|
||||
// https://sebhastian.com/javascript-delete-file/
|
||||
console.log("Deleting encrypted file");
|
||||
unlinkSync(fileNameDec);
|
||||
console.log("Deleting 1pt file");
|
||||
unlinkSync(padNameDec);
|
||||
}else {
|
||||
console.log("The decrypted file is: \n\n")
|
||||
console.log(keyDec);
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
console.log("Invalid action");
|
||||
help();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display help messages
|
||||
*/
|
||||
const help = ():void => {
|
||||
console.log("\n\n\n1tp is a tool used to encrypt and decrypt text files");
|
||||
console.log("Look at the commands below to learn the use of it");
|
||||
console.log("The input file for encryption should be a txt file");
|
||||
console.log("The output will be a .kye file and a .1pt file");
|
||||
console.log("\n\n**Note: Don't add file extensios while running the command**");
|
||||
console.log("**Note: if during creation, the application encouters a file already existing, it will overwrite the file**");
|
||||
console.log("Options: ");
|
||||
console.log("\t -h | --help \t\t\t\t\t\t\t View help");
|
||||
console.log("\t --generate <pad name> <size of file> \t\t\t\t Create a pad file given a size");
|
||||
console.log("\t --encrypt <txt file> <pad name> \t\t\t\t Encrypt a file give a pad file");
|
||||
console.log("\t\t\t\t All parameters for encryption are required");
|
||||
console.log("\t --decrypt <key file> <pad name> -o <txt file> \t Decrypt a file give a key and 1tp files");
|
||||
console.log("\t\t\t\t If output file is not specified it will display the text inside the console");
|
||||
console.log("\t\t\t\t If only the `-o` option is defined without a txt file, it will create a new file");
|
||||
console.log("\n** Note: Decrypting a file will delete the key and the 1pt files**");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file with a random key
|
||||
* @param file Name of the file being created
|
||||
* @param fileSize Size of the file being created
|
||||
*/
|
||||
const createFile = (file:string, fileSize:number = 1024, key:boolean = true, data:string = ""):void => {
|
||||
let fileName:string;
|
||||
if(key){
|
||||
fileName = `${file.trim().toLowerCase()}${padExtension}`;
|
||||
|
||||
// https://flaviocopes.com/how-to-check-if-file-exists-node/#:~:text=The%20way%20to%20check%20if%20a%20file%20exists,the%20existence%20of%20a%20file%20without%20opening%20it%3A
|
||||
|
||||
console.log("Generating the One time pad");
|
||||
const pad = generateOneTimePad(fileSize);
|
||||
//https://code-boxx.com/create-save-files-javascript/#:~:text=%20The%20possible%20ways%20to%20create%20and%20save,the%20server.%0Avar%20data%20%3D%20new%20FormData...%20More%20
|
||||
console.log("Writing to file");
|
||||
writeFileSync(fileName, pad);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
console.log("Writing to file");
|
||||
writeFileSync(file, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a pad
|
||||
* @param fileSize Size of the new pad being created
|
||||
* @returns the new pad
|
||||
*/
|
||||
const generateOneTimePad = (fileSize:number):string => {
|
||||
/**
|
||||
* String in which we will put the one time pad
|
||||
*/
|
||||
let otp:string = "";
|
||||
/**
|
||||
* used to organize the key
|
||||
*/
|
||||
let splitCounter = 0;
|
||||
let columnCounter = 0;
|
||||
|
||||
for (let i = 0; i <= fileSize;) {
|
||||
if(splitCounter < 6){
|
||||
// Generate a random letter from the alphabet given
|
||||
otp += (stringLetter.charAt(Math.random()*1000%stringLetter.length))
|
||||
splitCounter++;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// Used to organize the key so it looks estetic
|
||||
if(columnCounter < 7){
|
||||
splitCounter = 0;
|
||||
columnCounter++;
|
||||
otp += ("\t");
|
||||
continue;
|
||||
}else{
|
||||
splitCounter = 0;
|
||||
columnCounter = 0;
|
||||
otp += ("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
return otp
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pad data from the file
|
||||
* @returns Text pad data
|
||||
*/
|
||||
const RetrievePadData = (padFile:string):string => {
|
||||
// Retrieve the pad data
|
||||
let padData = ""
|
||||
// read the file
|
||||
const data = readFileSync(`${padFile}`, 'utf-8')
|
||||
|
||||
// Take out all the tabs and new lines we added while generating the key
|
||||
const stringArray = data.toString().split("\n");
|
||||
for (let index = 0; index < stringArray.length; index++) {
|
||||
const element = stringArray[index].split("\t");
|
||||
for (let i = 0; i < element.length; i++) {
|
||||
padData += element[i];
|
||||
}
|
||||
}
|
||||
|
||||
return padData;
|
||||
}
|
||||
|
||||
/**
|
||||
* One time pad function
|
||||
* @param fileData Data of the file to encrypt or decrypt
|
||||
* @param padData Data of the pad file
|
||||
* @returns The generated encryption or decryption
|
||||
*/
|
||||
const padFunction = (fileData:string, padData:string ):string => {
|
||||
let key = "";
|
||||
for (let i = 0; i < fileData.length; i++) {
|
||||
key += String.fromCharCode(-fileData[i].charCodeAt(0)+padData[i].toLowerCase().charCodeAt(0)+97);
|
||||
// console.log(`File char: ${fileData[i]} ${fileData[i].charCodeAt(0)} Pad Data: ${padData[i]} ${padData[i].toLocaleLowerCase().charCodeAt(0)} Key: ${key[i]} ${key[i].charCodeAt(0)}`)
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
main();
|
||||
195
Task/One-time-pad/Wren/one-time-pad.wren
Normal file
195
Task/One-time-pad/Wren/one-time-pad.wren
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
import "io" for File, Directory
|
||||
import "/srandom" for SRandom
|
||||
import "/ioutil" for FileUtil, Input
|
||||
import "/dynamic" for Enum
|
||||
import "/str" for Char, Str
|
||||
|
||||
var CHARS_PER_LINE = 48
|
||||
var CHUNK_SIZE = 6
|
||||
var COLS = 8
|
||||
var DEMO = true // would normally be set to false
|
||||
|
||||
var FileType = Enum.create("FileType", ["OTP", "ENC", "DEC"])
|
||||
|
||||
var toAlpha = Fn.new { |s| s.where { |c| Char.isAsciiUpper(c) }.join() }
|
||||
|
||||
var isOtpRelated = Fn.new { |s|
|
||||
return s.endsWith(".1tp") || s.endsWith(".1tp_cpy") ||
|
||||
s.endsWith(".1tp_enc") || s.endsWith(".1tp_dec")
|
||||
}
|
||||
|
||||
var inChunks = Fn.new { |s, nLines, ft|
|
||||
var chunks = Str.chunks(s, CHUNK_SIZE)
|
||||
var sb = ""
|
||||
for (i in 0...nLines) {
|
||||
var j = i * COLS
|
||||
var ch = chunks[j...j+COLS].join(" ")
|
||||
sb = sb + " " + ch + "\n"
|
||||
}
|
||||
sb = " file\n" + sb
|
||||
return (ft == FileType.OTP) ? "# OTP" + sb :
|
||||
(ft == FileType.ENC) ? "# Encrypted" + sb :
|
||||
(ft == FileType.DEC) ? "# Decrypted" + sb : ""
|
||||
}
|
||||
|
||||
var makePad = Fn.new { |nLines|
|
||||
var nChars = nLines * CHARS_PER_LINE
|
||||
var sb = ""
|
||||
/* generate random upper case letters */
|
||||
for (i in 0...nChars) sb = sb + String.fromByte(SRandom.int(65, 91))
|
||||
return inChunks.call(sb, nLines, FileType.OTP)
|
||||
}
|
||||
|
||||
var vigenere = Fn.new { |text, key, encrypt|
|
||||
var sb = ""
|
||||
var i = 0
|
||||
for (c in text) {
|
||||
var ci = encrypt ? (c.bytes[0] + key[i].bytes[0] - 130) % 26 :
|
||||
(c.bytes[0] - key[i].bytes[0] + 26) % 26
|
||||
sb = sb + String.fromByte(ci + 65)
|
||||
i = i + 1
|
||||
}
|
||||
var temp = sb.count % CHARS_PER_LINE
|
||||
if (temp > 0) { // pad with random characters so each line is a full one
|
||||
for (i in temp...CHARS_PER_LINE) sb = sb + String.fromByte(SRandom.int(65, 91))
|
||||
}
|
||||
var ft = encrypt ? FileType.ENC : FileType.DEC
|
||||
return inChunks.call(sb, (sb.count / CHARS_PER_LINE).floor, ft)
|
||||
}
|
||||
|
||||
var menu = Fn.new {
|
||||
System.print("""
|
||||
|
||||
1. Create one time pad file.
|
||||
|
||||
2. Delete one time pad file.
|
||||
|
||||
3. List one time pad files.
|
||||
|
||||
4. Encrypt plain text.
|
||||
|
||||
5. Decrypt cipher text.
|
||||
|
||||
6. Quit program.
|
||||
|
||||
""")
|
||||
return Input.integer("Your choice (1 to 6) : ", 1, 6)
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var choice = menu.call()
|
||||
System.print()
|
||||
if (choice == 1) { // Create OTP
|
||||
System.print("Note that encrypted lines always contain 48 characters.\n")
|
||||
var fileName = Input.text("OTP file name to create (without extension) : ", 1) + ".1tp"
|
||||
var nLines = Input.integer("Number of lines in OTP (max 1000) : ", 1, 1000)
|
||||
var key = makePad.call(nLines)
|
||||
File.create(fileName) { |f| f.writeBytes(key) }
|
||||
System.print("\n'%(fileName)' has been created in the current directory.")
|
||||
if (DEMO) {
|
||||
// a copy of the OTP file would normally be on a different machine
|
||||
var fileName2 = fileName + "_cpy" // copy for decryption
|
||||
File.create(fileName2) { |f| f.writeBytes(key) }
|
||||
System.print("'%(fileName2)' has been created in the current directory.")
|
||||
System.print("\nThe contents of these files are :\n")
|
||||
System.print(key)
|
||||
}
|
||||
} else if (choice == 2) { // Delete OTP
|
||||
System.print("Note that this will also delete ALL associated files.\n")
|
||||
var toDelete1 = Input.text("OTP file name to delete (without extension) : ", 1) + ".1tp"
|
||||
var toDelete2 = toDelete1 + "_cpy"
|
||||
var toDelete3 = toDelete1 + "_enc"
|
||||
var toDelete4 = toDelete1 + "_dec"
|
||||
var allToDelete = [toDelete1, toDelete2, toDelete3, toDelete4]
|
||||
var deleted = 0
|
||||
System.print()
|
||||
for (name in allToDelete) {
|
||||
if (File.exists(name)) {
|
||||
File.delete(name)
|
||||
deleted = deleted + 1
|
||||
System.print("'%(name)' has been deleted from the current directory.")
|
||||
}
|
||||
}
|
||||
if (deleted == 0) System.print("There are no files to delete.")
|
||||
} else if (choice == 3) { // List OTPs
|
||||
System.print("The OTP (and related) files in the current directory are:\n")
|
||||
var otpFiles = Directory.list("./").where { |f| File.exists(f) && isOtpRelated.call(f) }.toList
|
||||
System.print(otpFiles.join("\n")) // already sorted
|
||||
} else if (choice == 4) { // Encrypt
|
||||
var keyFile = Input.text("OTP file name to use (without extension) : ", 1) + ".1tp"
|
||||
if (File.exists(keyFile)) {
|
||||
var lines = FileUtil.readLines(keyFile)
|
||||
var first = lines.count
|
||||
for (i in 0...lines.count) {
|
||||
if (lines[i].startsWith(" ")) {
|
||||
first = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (first == lines.count) {
|
||||
System.print("\nThat file has no unused lines.")
|
||||
continue
|
||||
}
|
||||
var lines2 = lines.skip(first).toList // get rid of comments and used lines
|
||||
var text = toAlpha.call(Str.upper(Input.text("Text to encrypt :-\n\n", 1)))
|
||||
var len = text.count
|
||||
var nLines = (len / CHARS_PER_LINE).floor
|
||||
if (len % CHARS_PER_LINE > 0) nLines = nLines + 1
|
||||
if (lines2.count >= nLines) {
|
||||
var key = toAlpha.call(lines2.take(nLines).join(""))
|
||||
var encrypted = vigenere.call(text, key, true)
|
||||
var encFile = keyFile + "_enc"
|
||||
File.create(encFile) { |f| f.writeBytes(encrypted) }
|
||||
System.print("\n'%(encFile)' has been created in the current directory.")
|
||||
for (i in first...first + nLines) {
|
||||
lines[i] = "-" + lines[i][1..-1]
|
||||
}
|
||||
File.create(keyFile) { |f| f.writeBytes(lines.join("\n")) }
|
||||
if (DEMO) {
|
||||
System.print("\nThe contents of the encrypted file are :\n")
|
||||
System.print(encrypted)
|
||||
}
|
||||
} else System.print("Not enough lines left in that file to do encryption")
|
||||
} else System.print("\nhat file does not exist.")
|
||||
} else if (choice == 5) { // Decrypt
|
||||
var keyFile = Input.text("OTP file name to use (without extension) : ", 1) + ".1tp_cpy"
|
||||
if (File.exists(keyFile)) {
|
||||
var keyLines = FileUtil.readLines(keyFile)
|
||||
var first = keyLines.count
|
||||
for (i in 0...keyLines.count) {
|
||||
if (keyLines[i].startsWith(" ")) {
|
||||
first = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (first == keyLines.count) {
|
||||
System.print("\nThat file has no unused lines.")
|
||||
continue
|
||||
}
|
||||
var keyLines2 = keyLines[first..-1] // get rid of comments and used lines
|
||||
var encFile = keyFile[0..-4] + "enc"
|
||||
if (File.exists(encFile)) {
|
||||
var encLines = FileUtil.readLines(encFile)[1..-1] // exclude comment line
|
||||
var nLines = encLines.count
|
||||
if (keyLines2.count >= nLines) {
|
||||
var encrypted = toAlpha.call(encLines.join(""))
|
||||
var key = toAlpha.call(keyLines2.take(nLines).join(""))
|
||||
var decrypted = vigenere.call(encrypted, key, false)
|
||||
var decFile = keyFile[0..-4] + "dec"
|
||||
File.create(decFile) { |f| f.writeBytes(decrypted) }
|
||||
System.print("\n'%(decFile)' has been created in the current directory.")
|
||||
for (i in first...first + nLines) {
|
||||
keyLines[i] = "-" + keyLines[i][1..-1]
|
||||
}
|
||||
File.create(keyFile) { |f| f.writeBytes(keyLines.join("\n")) }
|
||||
if (DEMO) {
|
||||
System.print("\nThe contents of the decrypted file are :\n")
|
||||
System.print(decrypted)
|
||||
}
|
||||
} else System.print("Not enough lines left in that file to do decryption")
|
||||
} else System.print("\n'%(encFile)' is missing.")
|
||||
} else System.print("\nThat file does not exist.")
|
||||
} else {
|
||||
return // Quit
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue