Post-quantum signing utilities

A trio of utilities for generating SPHINCS⁺-SHAKE256-192f signatures.
This is a post-quantum, hash-based signature scheme with 194-bit
security.

It is also a test for using the Nimble package manager for managing Nim
target dependencies.
This commit is contained in:
Emery Hemingway
2018-07-06 15:48:16 +02:00
committed by Norman Feske
parent affa36eb2d
commit 2ba33d38a9
19 changed files with 372 additions and 0 deletions

31
mk/nimble.mk Normal file
View File

@@ -0,0 +1,31 @@
TARGET ?= $(lastword $(subst /, ,$(PRG_DIR)))
NIMBLE_PKG ?= $(TARGET)
LIBS = base libc
CC_CXX_WARN_STRICT =
$(TARGET): assemble.tag $(SHARED_LIBS)
libs=$(LIB_CACHE_DIR); $(LD_CMD) $(wildcard src/nimcache/*.o) -o $(TARGET)
ln -sf $(CURDIR)/$@ $(INSTALL_DIR)/$@
assemble.tag: copy.tag nim.cfg
nimble --verbose cpp src/$(TARGET)
@touch $@
nim.cfg:
rm -f $@
echo "-d:nimDebugDlOpen" >> $@
echo "--os:genode" >> $@
echo "--cpu:$(NIM_CPU)" >> $@
echo "--noCppExceptions" >> $@
echo "--noLinking" >> $@
echo "--passC:\"$(CXX_DEF) $(CC_CXX_OPT) $(INCLUDES) -fpermissive\"" >> $@
echo "$(NIM_OPT)" >> $@
NIMBLE_PATH := $(shell nimble path $(NIMBLE_PKG))
copy.tag:
nimble install -n https://github.com/ehmry/nim-genode
cp -avu $(PRG_DIR)/* .
.PHONY: nim.cfg

66
run/sphincs_verify.run Normal file
View File

@@ -0,0 +1,66 @@
installed_command nimble
build { nimble/sphincs_verify }
create_boot_directory
import_from_depot genodelabs/src/[base_src] \
genodelabs/src/init \
genodelabs/src/report_rom \
genodelabs/src/libc
install_config {
<config>
<parent-provides>
<service name="CPU"/>
<service name="LOG"/>
<service name="PD"/>
<service name="RAM"/>
<service name="ROM"/>
</parent-provides>
<default-route> <any-service> <parent/> <any-child/> </any-service> </default-route>
<start name="report_rom" caps="100">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config verbose="yes"/>
</start>
<start name="verify" caps="200">
<binary name="sphincs_verify"/>
<resource name="RAM" quantum="12M"/>
<config verbose="yes">
<libc stdout="/dev/log" stderr="/dev/null" rtc="/dev/null"/>
<vfs>
<tar name="test.tar"/>
<dir name="dev"> <log/> <null/> </dir>
</vfs>
<verify path="expect_valid.txt" pubkey="/nonexistent_pubkey"/>
<verify path="expect_valid.txt" pubkey="/dev/null"/>
<verify path="expect_valid.txt" pubkey="/pubkey"/>
<verify path="expect_invalid.txt" pubkey="/pubkey"/>
</config>
</start>
</config>
}
exec tar cf [run_dir]/genode/test.tar -C [genode_dir]/repos/world/src/nimble/sphincs_verify/test_data .
build_boot_image { sphincs_verify libc.lib.so vfs.lib.so pthread.lib.so }
append qemu_args " -nographic "
run_genode_until {</result>.*\n} 30
grep_output {\[init \-\> report_rom\]}
compare_output_to {
[init -> report_rom] report 'verify -> result'
[init -> report_rom] <result>
[init -> report_rom] <bad reason="public key unavailable" path="expect_valid.txt" />
[init -> report_rom] <bad reason="malformed public key" path="expect_valid.txt" />
[init -> report_rom] <good path="expect_valid.txt" />
[init -> report_rom] <bad reason="bad signature" path="expect_invalid.txt" />
[init -> report_rom] </result>
}

View File

@@ -0,0 +1,12 @@
# Package
version = "0.1.0"
author = "Emery Hemingway"
description = "A post-quantum drop-in-replacement for the Depot verify tool"
license = "GPLv3"
srcDir = "src"
bin = @["sphincs_keygen"]
# Dependencies
requires "nim >= 0.18.0", "sphincs"

View File

@@ -0,0 +1,17 @@
import std/streams
import sphincs/shake256_192f
let rngStr = openFileStream("/dev/random")
proc readDevRand(p: pointer; size: int) =
let n = rngStr.readData(p, size)
doAssert(n == size, "short read from RNG")
proc writeToFile[T](path: string; x: var T) =
let fs = openFileStream(path, fmWrite)
fs.writeData(x.addr, sizeof(x))
close fs
var pair = generateKeypair(readDevRand)
writeToFile("secret", pair)
writeToFile("public", pair.pk)
echo "secret key written to ./secret, public key written to ./public"

View File

@@ -0,0 +1,3 @@
include $(call select_from_repositories,mk/nimble.mk)
LIBS += base libc

View File

@@ -0,0 +1,12 @@
# Package
version = "0.1.0"
author = "Emery Hemingway"
description = "A post-quantum drop-in-replacement for the Depot verify tool"
license = "GPLv3"
srcDir = "src"
bin = @["sphincs_sign"]
# Dependencies
requires "nim >= 0.18.0", "sphincs", "nimcrypto"

View File

@@ -0,0 +1,16 @@
import std/streams
import sphincs/shake256_256f
let rngStr = openFileStream("/dev/random")
proc readDevRand(p: pointer; size: int) =
let n = rngStr.readData(p, size)
doAssert(n == size, "short read from RNG")
proc writeToFile[T](path: string; x: var T) =
let fs = openFileStream(path, fmWrite)
fs.writeData(x.addr, sizeof(x))
close fs
var pair = shake256_256f.generateKeypair(readDevRand)
writeToFile("secret", pair)
writeToFile("public", pair.pk)

View File

@@ -0,0 +1,53 @@
import std/parseopt, std/streams
import sphincs/shake256_192f
import nimcrypto.hash, nimcrypto/keccak
proc hashFile(path: string): string =
let fs = openFileStream(path, fmRead)
result = newString(32)
var
ctx: sha3_256
buf: array[512, byte]
let bp = addr buf[0]
init ctx
while not fs.atEnd:
let n = fs.readData(bp, buf.len)
ctx.update(bp, n.uint)
close fs
var d = finish ctx
copyMem(result[0].addr, d.data[0].addr, result.len)
proc readPair(path: string): KeyPair =
let fs = openFileStream path
defer: close fs
doAssert(fs.readData(result.addr, sizeof(result)) == sizeof(result))
let rngStr = openFileStream("/dev/random")
proc readDevRand(p: pointer; size: int) =
let n = rngStr.readData(p, size)
doAssert(n == size, "short read from RNG")
proc signPath(pair: KeyPair; path: string) =
let
digest = hashFile path
sig = pair.sign(digest, readDevRand)
writeFile(path & ".sphincs", sig)
proc main() =
var
pair: KeyPair
argi = 0
for kind, key, val in getopt():
if kind != cmdArgument:
quit("invalid argument " & key & val)
if argi == 0:
pair = readPair(key)
else:
signPath(pair, key)
inc argi
if argi == 0:
echo "usage: sphincs_sign [SECRET_KEY] [FILE]"
main()

View File

@@ -0,0 +1,69 @@
import std/xmltree, std/xmlparser, std/streams
import sphincs/shake256_256f
import nimcrypto.hash, nimcrypto/keccak
proc hashFile(path: string): string =
let fs = openFileStream(path, fmRead)
result = newString(32)
var
ctx: sha3_256
buf: array[512, byte]
let bp = addr buf[0]
init ctx
while not fs.atEnd:
let n = fs.readData(bp, buf.len)
ctx.update(bp, n.uint)
close fs
var d = finish ctx
copyMem(result[0].addr, d.data[0].addr, result.len)
proc readPk(path: string): Pk =
let fs = openFileStream path
defer: close fs
doAssert(fs.readData(result.addr, sizeof(result)) == sizeof(result))
proc verify(filePath, pkPath: string): XmlNode =
try:
let
pk = readPk(pkPath)
sig = readFile(filePath & ".spx")
let
(valid, msg) = pk.verify sig
if not valid:
result = <>bad(path=filePath, reason="invalid signature")
else:
let digest = hashFile(filePath)
if digest == msg:
result = <>good(path=filePath)
else:
result = <>bad(path=filePath, reason="invalid digest")
except:
let e = getCurrentException()
result = <>bad(path=filePath, reason=e.msg)
when defined(genode):
import genode/reports, genode/roms
proc xml(rom: RomClient): XmlNode =
let s = rom.newStream
result = s.parseXml
close s
componentConstructHook = proc (env: GenodeEnv) =
let report = env.newReportClient("result")
proc handleConfig(rom: RomClient) =
let nodes = rom.xml.findAll("verify")
var results = newSeq[XmlNode](nodes.len)
for i, x in nodes.pairs:
results[i] = verify(x.attr("path"), x.attr("pubkey"))
report.submit do (s: Stream):
s.writeLine("<result>")
for r in results.items:
s.writeLine(r)
s.writeLine("</result>")
let
configHandler = env.newRomHandler("config", handleConfig)
process configHandler

View File

@@ -0,0 +1,3 @@
include $(call select_from_repositories,mk/nimble.mk)
LIBS += base libc

View File

@@ -0,0 +1,12 @@
# Package
version = "0.1.0"
author = "Emery Hemingway"
description = "A post-quantum drop-in-replacement for the Depot verify tool"
license = "GPLv3"
srcDir = "src"
bin = @["sphincs_verify"]
# Dependencies
requires "nim >= 0.18.0", "genode >= 18.7", "sphincs", "nimcrypto"

View File

@@ -0,0 +1,71 @@
import std/xmltree, std/xmlparser, std/streams
import sphincs/shake256_192f
import nimcrypto.hash, nimcrypto/keccak
proc hashFile(path: string): string =
let fs = openFileStream(path, fmRead)
result = newString(32)
var
ctx: sha3_256
buf: array[512, byte]
let bp = addr buf[0]
init ctx
while not fs.atEnd:
let n = fs.readData(bp, buf.len)
ctx.update(bp, n.uint)
close fs
var d = finish ctx
copyMem(result[0].addr, d.data[0].addr, result.len)
proc readPk(path: string): Pk =
let fs = newFileStream path
if fs.isNil: raiseAssert("public key unavailable")
defer: close fs
if fs.readData(result.addr, sizeof(result)) != sizeof(result):
raiseAssert("malformed public key")
proc verify(filePath, pkPath: string): XmlNode =
try:
let
pk = readPk(pkPath)
sig = readFile(filePath & ".sphincs")
let
(valid, msg) = pk.verify sig
if not valid:
result = <>bad(path=filePath, reason="bad signature")
else:
let digest = hashFile(filePath)
if digest == msg:
result = <>good(path=filePath)
else:
result = <>bad(path=filePath, reason="file digest mismatch")
except:
let e = getCurrentException()
result = <>bad(path=filePath, reason=e.msg)
when defined(genode):
import genode/reports, genode/roms
proc xml(rom: RomClient): XmlNode =
let s = rom.newStream
result = s.parseXml
close s
componentConstructHook = proc (env: GenodeEnv) =
let report = env.newReportClient("result")
proc handleConfig(rom: RomClient) =
let nodes = rom.xml.findAll("verify")
var results = newSeq[XmlNode](nodes.len)
for i, x in nodes.pairs:
results[i] = verify(x.attr("path"), x.attr("pubkey"))
report.submit do (s: Stream):
let xml = <>result()
for r in results.items:
xml.add(r)
s.writeLine(xml)
let
configHandler = env.newRomHandler("config", handleConfig)
process configHandler

View File

@@ -0,0 +1,3 @@
include $(call select_from_repositories,mk/nimble.mk)
LIBS += base libc

View File

@@ -0,0 +1 @@
Streifenhoernchen sind lecker

View File

@@ -0,0 +1 @@
Kinder moegen Suessigkeiten

View File

@@ -0,0 +1 @@
rOÛâ :áI³£<C2B3>ÈaþÎSFzw—cw}ÙB)i¨V_ž—ÿ]žŒ÷TÃç

View File

@@ -0,0 +1 @@
>Q¢”C¶HµD‰¸ v6‡ÉöûºÝàŒ½Þ´R¯ñãÀüZR#ÜîÓŠ¯øxâÑìrOÛâ :áI³£<C2B3>ÈaþÎSFzw—cw}ÙB)i¨V_ž—ÿ]žŒ÷TÃç