Csaba Kiraly 0ce3852e4f
using normal file io instead of memfiles
mmap has serveral potential issues and we do not really need it, so
changing to use the normal system file interface is better.

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
2022-01-17 11:26:10 -06:00

196 lines
5.7 KiB
Nim

## Nim-POS
## Copyright (c) 2021 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
import libp2p/crypto/crypto # for RSA
import bearssl
import nimcrypto # for SHA512
import random
import ./bigint/stint2
#import ./bigint/bigints2
const keysize = 2048
const sectorsperblock = 4.int64
const bytespersector = 128
const querylen = 22
assert bytespersector < keysize div 8 # TODO: not strict
type ZChar = array[bytespersector, byte]
proc fromBytesBE(nptr: ptr cuchar, nlen: int): BigInt =
let nptra = cast[ptr array[0xffffffff,byte]](nptr)
result = fromBytesBE(nptra[], nlen)
proc getSector(f: File, blockid: int64, sectorid: int64, spb: int64): ZChar =
f.setFilePos(blockid * spb + sectorid)
let r = f.readBytes(result, 0, sizeof(result))
proc fromBytesBE(sector: ZChar): BigInt =
result = fromBytesBE(sector, sizeof(ZChar))
proc getModulus(pubkey: PublicKey): BigInt =
result = fromBytesBE(pubkey.rsakey.key.n, pubkey.rsakey.key.nlen)
proc getModulus(seckey: PrivateKey): BigInt =
result = fromBytesBE(seckey.rsakey.pubk.n, seckey.rsakey.pubk.nlen)
proc getPubex(pubkey: PublicKey): BigInt =
## get RSA E exponent
result = fromBytesBE(pubkey.rsakey.key.e, pubkey.rsakey.key.elen)
proc getPrivex(seckey: PrivateKey): BigInt =
## get RSA D exponent
result = fromBytesBE(seckey.rsakey.pexp, seckey.rsakey.pexplen)
proc rsaDecode(msg: var array[256,byte], ssk: PrivateKey): array[256,byte] =
let RsaPrivate = rsaPrivateGetDefault()
let r = RsaPrivate(cast[ptr cuchar](addr(msg)), addr(ssk.rsakey.seck))
result = msg
proc rsaDecode(msg: BigInt, ssk: PrivateKey): BigInt =
assert msg < ssk.getModulus()
var msgarray = msg.to256BytesBE()
let enc = rsaDecode(msgarray, ssk)
result = fromBytesBE(enc, 256)
assert result < ssk.getModulus()
proc rsaEncode(msg: var array[256,byte], spk: PublicKey): array[256,byte] =
let RsaPublic = rsaPublicGetDefault()
let r = RsaPublic(cast[ptr cuchar](addr(msg)), 256, addr(spk.rsakey.key))
result = msg
proc rsaEncode(msg: BigInt, spk: PublicKey): BigInt =
assert msg < spk.getModulus()
var msgarray = msg.to256BytesBE()
let enc = rsaEncode(msgarray, spk)
result = fromBytesBE(enc, 256)
assert result < spk.getModulus()
type TauZero = object
name: array[512,byte]
n: int64
u: seq[BigInt]
type Tau = object
t: TauZero
signature: array[512, byte]
proc rsaKeygen*(): (PublicKey, PrivateKey) =
let rng = newRng()
var seckey = PrivateKey.random(RSA, rng[], keysize).get()
var pubkey = seckey.getKey().get()
return (pubkey, seckey)
proc split(f: File): (int64, int64) =
let size = f.getFileSize()
let n = ((size - 1) div (sectorsperblock * sizeof(ZChar))) + 1
return (sectorsperblock, n)
proc hashNameI(name: openArray[byte], i: int64): BigInt =
let hashString = $sha512.digest($name & $i)
return fromBytesBE(cast[seq[byte]](hashString), hashString.len()) # TODO: use better way to convert
proc generateAuthenticator(i: int64, s: int64, t: TauZero, f: File, ssk: PrivateKey): BigInt =
let N = ssk.getModulus()
var productory = BigInt.one
for j in 0 ..< s:
productory = mulmod(productory,
powmod(t.u[j], fromBytesBE(getSector(f, i, j, s)), N),
N)
# result = (hashNameI(t.name, i) * productory).powmod(getPrivex(ssk), N)
result = rsaDecode((hashNameI(t.name, i) * productory) mod N, ssk)
proc st*(ssk: PrivateKey, filename: string): (Tau, seq[BigInt]) =
let file = open(filename)
let (s, n) = split(file)
var t = TauZero(n: n)
# generate a random name
for i in 0 ..< 512 :
t.name[i] = rand(byte)
# generate the coefficient vector for combining sectors of a block: U
for i in 0 ..< s :
t.u.add(initBigInt(rand(uint32))) #TODO: fix limit
#TODO: sign for tau
let tau = Tau(t: t)
#generate sigmas
var sigmas: seq[BigInt]
for i in 0 ..< n :
sigmas.add(generateAuthenticator(i, s, t, file, ssk)) #TODO: int64 sizes?
file.close()
result = (tau, sigmas)
type QElement = object
I: int64
V: BigInt
proc generateQuery*(
tau: Tau,
spk: PublicKey,
l: int = querylen # query elements
): seq[QElement] =
# verify signature on Tau
let n = tau.t.n # number of blocks
for i in 0 ..< l :
var q: QElement
q.I = rand((int)n-1) #TODO: dedup
q.V = initBigInt(rand(uint64)) #TODO: fix range
result.add(q)
proc generateProof*(q: openArray[QElement], authenticators: openArray[BigInt], spk: PublicKey, filename: string): (seq[BigInt], BigInt) =
let file = open(filename)
let N = spk.getModulus()
let s = sectorsperblock
var mu: seq[BigInt]
for j in 0 ..< s :
var muj = BigInt.zero
for qelem in q :
let sector = fromBytesBE(getSector(file, qelem.I, j, s))
muj += qelem.V * sector
#muj = addmod(muj, mulmod(qelem.V, sector, N), N)
mu.add(muj)
var sigma = BigInt.one
for qelem in q:
sigma = mulmod(sigma,
powmod(authenticators[qelem.I], qelem.V, N),
N)
file.close()
return (mu, sigma)
proc verifyProof*(tau: Tau, q: openArray[QElement], mus: openArray[BigInt], sigma: BigInt, spk: PublicKey): bool =
# TODO: check that values are in range
let N = spk.getModulus()
var first = BigInt.one
for qelem in q :
first = mulmod(first,
powmod(hashNameI(tau.t.name, qelem.I), qelem.V, N),
N)
let us = tau.t.u
var second = BigInt.one
for j in 0 ..< len(us) :
second = mulmod(second,
powmod(us[j], mus[j], N),
N)
return mulmod(first, second, N) == rsaEncode(sigma, spk)