Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue