From 6559ff6f045bf32920fa59da95c103a823127fee Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 31 Aug 2021 13:03:14 +0200 Subject: [PATCH] initial commit of the Shacham RSA-based public scheme Minimal working version with lots of error checks and corrections still needed. - using Bearssl RSA code through libp2p - with selecteble BigInt library for experimentation Signed-off-by: Csaba Kiraly --- dagger/storageproofs/README.md | 1 + dagger/storageproofs/bigint/bigints2.nim | 46 +++++ dagger/storageproofs/bigint/stint2.nim | 22 +++ dagger/storageproofs/example.txt | 1 + dagger/storageproofs/pos.nim | 217 +++++++++++++++++++++++ 5 files changed, 287 insertions(+) create mode 100644 dagger/storageproofs/README.md create mode 100644 dagger/storageproofs/bigint/bigints2.nim create mode 100644 dagger/storageproofs/bigint/stint2.nim create mode 100644 dagger/storageproofs/example.txt create mode 100644 dagger/storageproofs/pos.nim diff --git a/dagger/storageproofs/README.md b/dagger/storageproofs/README.md new file mode 100644 index 00000000..1ee54b5e --- /dev/null +++ b/dagger/storageproofs/README.md @@ -0,0 +1 @@ +Nim implementation of Proof of Storage related schemes diff --git a/dagger/storageproofs/bigint/bigints2.nim b/dagger/storageproofs/bigint/bigints2.nim new file mode 100644 index 00000000..102bfcbd --- /dev/null +++ b/dagger/storageproofs/bigint/bigints2.nim @@ -0,0 +1,46 @@ +## 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 bigints +export bigints + +func zero*(T: typedesc): T {.inline.} = + bigints.zero + +func one*(T: typedesc): T {.inline.} = + bigints.one + +func mulmod*(a, b, m: BigInt): BigInt = + (a * b) mod m + +proc powmod*(b, e, m: BigInt): BigInt = + assert e >= 0 + var e = e + var b = b + result = bigints.one + while e > 0: + if e mod 2 == 1: + result = (result * b) mod m + e = e div 2 + b = (b.pow 2) mod m + +proc fromBytesBE*(nptr: openArray[byte], nlen: int): BigInt = + result = bigints.zero + for i in 0 ..< nlen: + result = result * 256 + cast[int32](nptr[i]) + +proc to256BytesBE*(n: BigInt): array[256, byte] = + var nn = n + + let nlen = 256 + for i in 0 ..< nlen: + result[nlen - 1 - i] = cast[uint8](nn.limbs[0] mod 256) + nn = nn div 256 + # if nn == 0: + # break diff --git a/dagger/storageproofs/bigint/stint2.nim b/dagger/storageproofs/bigint/stint2.nim new file mode 100644 index 00000000..700f66a7 --- /dev/null +++ b/dagger/storageproofs/bigint/stint2.nim @@ -0,0 +1,22 @@ +## 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 stint +export stint + +type BigInt* = StUInt[4096] #TODO: check why 2048 is not enough + +proc fromBytesBE*(x: openArray[byte], l: int): BigInt = + result = BigInt.fromBytesBE(x[0..l-1]) + +proc to256BytesBE*(msg: BigInt): array[256, byte] = + stuint(msg, 2048).toBytesBE() + +proc initBigInt*(n: SomeInteger): (BigInt) = + result = stuint(n, BigInt.bits) diff --git a/dagger/storageproofs/example.txt b/dagger/storageproofs/example.txt new file mode 100644 index 00000000..5be377b5 --- /dev/null +++ b/dagger/storageproofs/example.txt @@ -0,0 +1 @@ +The quick brown fox jumps over the lazy dog! diff --git a/dagger/storageproofs/pos.nim b/dagger/storageproofs/pos.nim new file mode 100644 index 00000000..5851e714 --- /dev/null +++ b/dagger/storageproofs/pos.nim @@ -0,0 +1,217 @@ +## 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 memfiles +import math +import nimcrypto # for SHA512 +import random + +import ./bigint/stint2 +#import ./bigint/bigints2 + +const keysize = 2048 +const sectorsperblock = 4 +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(filep: ptr ZChar, blockid: int64, sectorid: int64, spb: int64): Zchar = + result = cast[ptr array[0xffffffff, ZChar]](filep)[blockid * spb + sectorid] + +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 openFile(file: string, s = sectorsperblock, c = sizeof(ZChar)): (ptr ZChar, int64, int64) = + let mm = memfiles.open(file) + + let size = mm.size + let n = int64(ceil(float64(size / (s * c)))) + + return (cast[ptr ZChar](mm.mem), int64(s), 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, filep: ptr ZChar, 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(filep, 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, file: string): (Tau, seq[BigInt]) = + let (filep, s, n) = openFile(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, filep, ssk)) #TODO: int64 sizes? + + 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, file: string): (seq[BigInt], BigInt) = + let (filep, s, _) = openFile(file) + let N = spk.getModulus() + + var mu: seq[BigInt] + for j in 0 ..< s : + var muj = BigInt.zero + for qelem in q : + let sector = fromBytesBE(getSector(filep, 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) + + return (mu, sigma) + +proc Verify_two(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) + +proc test() : bool = + let (spk, ssk) = pos.rsaKeygen() + echo "Key generated!" + + let (tau, authenticators) = pos.st(ssk, "example.txt") + echo "Signed!" + echo "Auth: ", authenticators + + echo "Generating challenge..." + let q = pos.generateQuery(tau, spk) + echo "Generated!", " q:", q + + echo "Issuing proof..." + let (mu, sigma) = pos.generateProof(q, authenticators, spk, "example.txt") + echo "Issued!", " mu:", mu, " sigma:", sigma + + echo "Verifying proof..." + result = pos.Verify_two(tau, q, mu, sigma, spk) + echo "Result: ", result + +randomize() +let r = test() \ No newline at end of file