No commit suggestions generated

This commit is contained in:
Prad Nukala
2025-10-09 15:10:39 -04:00
commit a934caa7d3
323 changed files with 98121 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
---
aliases: [README]
tags: []
title: README
linter-yaml-title-alias: README
date created: Wednesday, April 17th 2024, 4:11:40 pm
date modified: Thursday, April 18th 2024, 8:19:25 am
---
## Paillier Cryptosystem
Package paillier contains [Paillier's cryptosystem (1999)](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.112.4035&rep=rep1&type=pdf).
All routines here from pseudocode §2.5. Fig 1: The Paillier Cryptosystem.
This module provides APIs for:
- generating a safe key pair
- encryption and decryption
- adding two encrypted values, `Enc(a)` and `Enc(b)`, and obtaining `Enc(a + b)`, and
- multiplying a plain value, `a`, and an encrypted value `Enc(b)`, and obtaining `Enc(a * b)`.
The encrypted values are represented as `big.Int` and are serializable.
This module also provides JSON serialization for the PublicKey and the SecretKey.
+377
View File
@@ -0,0 +1,377 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package paillier contains Paillier's cryptosystem (1999) [P99].
// Public-Key Cryptosystems Based on Composite Degree Residuosity Class.
// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.112.4035&rep=rep1&type=pdf
// All routines here from pseudocode §2.5. Fig 1: The Paillier Cryptosystem.
//
// This module provides APIs for:
//
// - generating a safe keypair,
// - encryption and decryption,
// - adding two encrypted values, Enc(a) and Enc(b), and obtaining Enc(a + b), and
// - multiplying a plain value, a, and an encrypted value Enc(b), and obtaining Enc(a * b).
//
// The encrypted values are represented as big.Int and are serializable. This module also provides
// JSON serialization for the PublicKey and the SecretKey.
package paillier
import (
"encoding/json"
"fmt"
"math/big"
"github.com/pkg/errors"
"github.com/sonr-io/sonr/crypto/core"
"github.com/sonr-io/sonr/crypto/internal"
)
// PaillierPrimeBits is the number of bits used to generate Paillier Safe Primes.
const PaillierPrimeBits = 1024
type (
// PublicKey is a Paillier public key: N = P*Q; for safe primes P,Q.
PublicKey struct {
N *big.Int // N = PQ
N2 *big.Int // N² computed and cached to prevent re-computation.
}
// PublicKeyJson encapsulates the data that is serialized to JSON.
// It is used internally and not for external use. Public so other pieces
// can use for serialization.
PublicKeyJson struct {
N *big.Int
}
// SecretKey is a Paillier secret key.
SecretKey struct {
PublicKey
Lambda *big.Int // lcm(P - 1, Q - 1)
Totient *big.Int // Euler's totient: (P - 1) * (Q - 1)
U *big.Int // L((N + 1)^λ(N) mod N²)1 mod N
}
// SecretKeyJson encapsulates the data that is serialized to JSON.
// It is used internally and not for external use. Public so other pieces
// can use for serialization.
SecretKeyJson struct {
N, Lambda, Totient, U *big.Int
}
// Ciphertext in Pailler's cryptosystem: a value $c \in Z_{N²}$ .
Ciphertext *big.Int
)
// NewKeys generates Paillier keys with `bits` sized safe primes.
func NewKeys() (*PublicKey, *SecretKey, error) {
return keyGenerator(core.GenerateSafePrime, PaillierPrimeBits)
}
// keyGenerator generates Paillier keys with `bits` sized safe primes using function
// `genSafePrime` to generate the safe primes.
func keyGenerator(
genSafePrime func(uint) (*big.Int, error),
bits uint,
) (*PublicKey, *SecretKey, error) {
values := make(chan *big.Int, 2)
errors := make(chan error, 2)
var p, q *big.Int
for p == q {
for range []int{1, 2} {
go func() {
value, err := genSafePrime(bits)
values <- value
errors <- err
}()
}
for _, err := range []error{<-errors, <-errors} {
if err != nil {
return nil, nil, err
}
}
p, q = <-values, <-values
}
// Assemble the secret/public key pair.
sk, err := NewSecretKey(p, q)
if err != nil {
return nil, nil, err
}
return &sk.PublicKey, sk, nil
}
// NewSecretKey computes intermediate values based on safe primes p, q.
func NewSecretKey(p, q *big.Int) (*SecretKey, error) {
if p == nil || q == nil {
return nil, internal.ErrNilArguments
}
// Pre-compute necessary values.
pm1 := new(big.Int).Sub(p, core.One) // P - 1
qm1 := new(big.Int).Sub(q, core.One) // Q - 1
n := new(big.Int).Mul(p, q) // N = PQ
nn := new(big.Int).Mul(n, n) // N²
lambda, err := lcm(pm1, qm1) // λ(N) = lcm(P-1, Q-1)
if err != nil {
// Code coverage note: lcm returns error only if the inputs are nil, which can never happen here.
return nil, err
}
totient := new(big.Int).Mul(pm1, qm1) // 𝝋(N) = (P-1)(Q-1)
pk := PublicKey{
N: n,
N2: nn,
}
// (N+1)^λ(N) mod N²
t := new(big.Int).Add(n, core.One)
t.Exp(t, lambda, nn)
// L((N+1)^λ(N) mod N²)
u, err := pk.l(t)
if err != nil {
return nil, err
}
// L((N+1)^λ(N) mod N²)^-1 mod N
u.ModInverse(u, n)
return &SecretKey{pk, lambda, totient, u}, nil
}
// MarshalJSON converts the public key into json format.
func (pk PublicKey) MarshalJSON() ([]byte, error) {
data := PublicKeyJson{pk.N}
return json.Marshal(data)
}
// UnmarshalJSON converts the json data into this public key.
func (pk *PublicKey) UnmarshalJSON(bytes []byte) error {
data := new(PublicKeyJson)
if err := json.Unmarshal(bytes, data); err != nil {
return err
}
if data.N == nil {
return nil
}
pk.N = data.N
pk.N2 = new(big.Int).Mul(data.N, data.N)
return nil
}
// lcm calculates the least common multiple.
func lcm(x, y *big.Int) (*big.Int, error) {
if x == nil || y == nil {
return nil, internal.ErrNilArguments
}
gcd := new(big.Int).GCD(nil, nil, x, y)
if core.ConstantTimeEq(gcd, core.Zero) {
return core.Zero, nil
}
// Compute least common multiple: https://en.wikipedia.org/wiki/Least_common_multiple#Calculation .
b := new(big.Int)
return b.Abs(b.Mul(b.Div(x, gcd), y)), nil
}
// l computes a residuosity class of n^2: (x - 1) / n.
// Where it is the quotient x - 1 divided by n not modular multiplication of x - 1 times
// the modular multiplicative inverse of n. The function name comes from [P99].
func (pk *PublicKey) l(x *big.Int) (*big.Int, error) {
if x == nil {
return nil, internal.ErrNilArguments
}
if core.ConstantTimeEq(pk.N, core.Zero) {
return nil, internal.ErrNCannotBeZero
}
// Ensure x = 1 mod N
if !core.ConstantTimeEq(new(big.Int).Mod(x, pk.N), core.One) {
return nil, internal.ErrResidueOne
}
// Ensure x ∈ Z_N²
if err := core.In(x, pk.N2); err != nil {
return nil, err
}
// (x - 1) / n
b := new(big.Int).Sub(x, core.One)
return b.Div(b, pk.N), nil
}
// NewPubkey initializes a Paillier public key with a given n.
func NewPubkey(n *big.Int) (*PublicKey, error) {
if n == nil {
return nil, errors.New("n cannot be nil")
}
return &PublicKey{
N: n,
N2: new(big.Int).Mul(n, n), // Compute and cache N²
}, nil
}
// Add combines two Paillier ciphertexts.
func (pk *PublicKey) Add(c, d Ciphertext) (Ciphertext, error) {
if c == nil || d == nil {
return nil, internal.ErrNilArguments
}
// Ensure c,d ∈ Z_N²
cErr := core.In(c, pk.N2)
dErr := core.In(d, pk.N2)
// Constant time error check
var err error
if cErr != nil {
err = cErr
}
if dErr != nil {
err = dErr
}
if err != nil {
return nil, err
}
ctxt, err := core.Mul(c, d, pk.N2)
if err != nil {
// Code coverage note: core.Mul returns error only if the inputs are nil, which can never happen here.
return nil, err
}
return ctxt, nil
}
// Mul is equivalent to adding two Paillier exponents.
func (pk *PublicKey) Mul(a *big.Int, c Ciphertext) (Ciphertext, error) {
if a == nil || c == nil {
return nil, internal.ErrNilArguments
}
// Ensure a ∈ Z_N
aErr := core.In(a, pk.N)
// Ensure c ∈ Z_N²
cErr := core.In(c, pk.N2)
var err error
// Constant time error check
if aErr != nil {
err = aErr
}
if cErr != nil {
err = cErr
}
if err != nil {
return nil, err
}
return new(big.Int).Exp(c, a, pk.N2), nil
}
// Encrypt produces a ciphertext on input message.
func (pk *PublicKey) Encrypt(msg *big.Int) (Ciphertext, *big.Int, error) {
// generate a nonce: r \in Z**_N
r, err := core.Rand(pk.N)
if err != nil {
return nil, nil, err
}
// Generate and return the ciphertext
ct, err := pk.encrypt(msg, r)
return ct, r, err
}
// encrypt produces a ciphertext on input a message and nonce.
func (pk *PublicKey) encrypt(msg, r *big.Int) (Ciphertext, error) {
if msg == nil || r == nil {
return nil, internal.ErrNilArguments
}
// Ensure msg ∈ Z_N
if err := core.In(msg, pk.N); err != nil {
return nil, err
}
// Ensure r ∈ Z^*_N: we use the method proved in docs/[EL20]
// ensure r ∈ Z^_N-{0}
if err := core.In(r, pk.N); err != nil {
return nil, err
}
if core.ConstantTimeEq(r, core.Zero) {
return nil, fmt.Errorf("r cannot be 0")
}
// Compute the ciphertext components: ɑ, β
// ɑ = (N+1)^m (mod N²)
ɑ := new(big.Int).Add(pk.N, core.One)
ɑ.Exp(ɑ, msg, pk.N2)
β := new(big.Int).Exp(r, pk.N, pk.N2) // β = r^N (mod N²)
// ciphertext = ɑ*β = (N+1)^m * r^N (mod N²)
c, err := core.Mul(ɑ, β, pk.N2)
if err != nil {
// Code coverage note: core.Mul returns error only if the inputs are nil, which can never happen here.
return nil, err
}
return c, nil
}
// Decrypt is the reverse operation of Encrypt.
func (sk *SecretKey) Decrypt(c Ciphertext) (*big.Int, error) {
if c == nil {
return nil, internal.ErrNilArguments
}
// Ensure C ∈ Z_N²
if err := core.In(c, sk.N2); err != nil {
return nil, err
}
// Compute the msg in components
// ɑ ≡ c^{λ(N)} mod N²
ɑ := new(big.Int).Exp(c, sk.Lambda, sk.N2)
// l = L(ɑ, N)
ell, err := sk.l(ɑ)
if err != nil {
return nil, err
}
// Compute the msg
// m ≡ lu = L(ɑ)*u = L(c^{λ(N)})*u mod N
m, err := core.Mul(ell, sk.U, sk.N)
if err != nil {
return nil, err
}
return m, nil
}
// MarshalJSON converts the secret key into json format.
func (sk SecretKey) MarshalJSON() ([]byte, error) {
data := SecretKeyJson{
sk.N,
sk.Lambda,
sk.Totient,
sk.U,
}
return json.Marshal(data)
}
// UnmarshalJSON converts the json data into this secret key.
func (sk *SecretKey) UnmarshalJSON(bytes []byte) error {
data := new(SecretKeyJson)
if err := json.Unmarshal(bytes, data); err != nil {
return err
}
if data.N != nil {
sk.N = data.N
sk.N2 = new(big.Int).Mul(data.N, data.N)
}
sk.U = data.U
sk.Totient = data.Totient
sk.Lambda = data.Lambda
return nil
}
+235
View File
@@ -0,0 +1,235 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// This file contains proofs that Paillier moduli are square-free: [spec] fig 15
package paillier
import (
"crypto/elliptic"
"fmt"
"math/big"
crypto "github.com/sonr-io/sonr/crypto/core"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/internal"
)
// [spec] 10.2 and ProvePSF, VerifyPSF fig.15
const PsfProofLength = 13
// PsfProofParams contains the inputs to ProvePSF
type PsfProofParams struct {
Curve elliptic.Curve
SecretKey *SecretKey
Pi uint32
Y *curves.EcPoint
}
// PsfVerifyParams contains the inputs to VerifyPSF
type PsfVerifyParams struct {
Curve elliptic.Curve
PublicKey *PublicKey
Pi uint32
Y *curves.EcPoint
}
// PsfProof is a slice of 13 big.Int's that prove that a Paillier modulus is square-free
type PsfProof []*big.Int
// Prove that a Paillier modulus is square-free
// [spec] §10.fig 15
func (p *PsfProofParams) Prove() (PsfProof, error) {
// Verify that params are sane
if p.Curve == nil ||
p.SecretKey == nil ||
p.Pi == 0 ||
p.Y == nil {
return nil, internal.ErrNilArguments
}
// 1. ell = 13
// Note this is set above as PsfProofLength
// 2. M = N^{-1} mod \phi(N)
M, err := crypto.Inv(p.SecretKey.N, p.SecretKey.Totient)
if err != nil {
return nil, err
}
// 3. [x_1, ..., x_ell] <- GenerateChallenges(g,q,y,Pi,ell)
// NOTE: spec doesn't include N, but it's an oversight--should be part of the
// commitment
x, err := generateChallenges(p.Curve.Params(), p.SecretKey.N, p.Pi, p.Y)
if err != nil {
return nil, err
}
if len(x) != PsfProofLength {
return nil, fmt.Errorf(
"challenges array is not correct length: want=%v got=%v",
PsfProofLength,
len(x),
)
}
// 4. For i = [1, ... \ell]
// NOTE: typo in spec: says j = ... but uses subscript i in loop
proof := make([]*big.Int, PsfProofLength)
for i, xj := range x {
// 5. Compute y_i = x_i^M mod N
// NOTE: the pseudocode shows mod phi(N) which is incorrect
// it should be mod N otherwise the reverse in Verify
// will fail. Using phi(N) puts M in the wrong group.
yi, err := crypto.Exp(xj, M, p.SecretKey.N)
if err != nil {
return nil, err
}
// 6. Set \Pi = [y_1, ..., y_\ell]
// NOTE: typo in spec: says y_t not y_\ell
proof[i] = yi
}
// 7. return \Pi
return proof, nil
}
// Verify that a Paillier modulus is square-free
// [spec] §10.fig 15
func (p PsfProof) Verify(psf *PsfVerifyParams) error {
// Verify that params are sane
if psf == nil ||
psf.Curve == nil ||
psf.PublicKey == nil ||
psf.Pi == 0 ||
psf.Y == nil {
return internal.ErrNilArguments
}
// 1. ell = 13
// Note this is set above as PsfProofLength
// 2. t = 1000
// NOTE not used anywhere
// 3. if q|N return false
if new(big.Int).Mod(psf.PublicKey.N, psf.Curve.Params().N).Cmp(crypto.Zero) == 0 {
return fmt.Errorf("paillier public key is a multiple of the curve subgroup")
}
// 4. [x_1, ..., x_ell] <- GenerateChallenges(g,q,y,Pi,ell)
// NOTE: spec doesn't include N, but it's an oversight--should be part of the
// commitment
x, err := generateChallenges(psf.Curve.Params(), psf.PublicKey.N, psf.Pi, psf.Y)
if err != nil {
return err
}
if len(x) != PsfProofLength {
return fmt.Errorf(
"challenges array is not correct length: want=%v got=%v",
PsfProofLength,
len(x),
)
}
// 5. for j in [1,...,l]
for j, xj := range x {
// 6. yj^N != x mod N return false
// NOTE: pseudocode uses i when loop uses j
lhs, err := crypto.Exp(p[j], psf.PublicKey.N, psf.PublicKey.N)
if err != nil {
return err
}
if lhs.Cmp(xj) != 0 {
return fmt.Errorf("not equal at %d", j)
}
}
return nil
}
// generateChallenges computes `l` deterministic numbers as
// challenges for PsfProof which proves that the Paillier modulus is square free
// [spec] fig.15 GenerateChallenges
func generateChallenges(
params *elliptic.CurveParams,
N *big.Int,
pi uint32,
y *curves.EcPoint,
) ([]*big.Int, error) {
if params == nil ||
y == nil ||
pi == 0 {
return nil, internal.ErrNilArguments
}
// 1. Set b = |N| // bit length of N
b := N.BitLen()
// a modulus that is too small turns this function into an infinite loop
// need at least a byte to guarantee termination
if b < 8 {
return nil, internal.ErrNilArguments
}
// 2. h = output bit-length of fiat-shamir hash
// See util.fiatShamir which uses sha256
// So the output bit-length is 256 bits
const h int = 256
// 3. Compute s = ⌈b/h⌉ // number of hash outputs required to obtain b bits
// i.e. the number of times we have to call fs-shamir to get the same bits as
// `b`. Compute ceil as ceilVal = (a+b-1) / b
s := int64((b + h - 1) / h)
// 4. j = 0
j := int64(0)
// 5. m = 0
m := big.NewInt(0)
x := make([]*big.Int, PsfProofLength)
Pi := new(big.Int).SetUint64(uint64(pi))
// 6. while j ≤ l
for j < PsfProofLength {
bij := big.NewInt(j)
var ej []byte
// 7. for k = [1,...,s]
for k := int64(1); k <= s; k++ {
bik := big.NewInt(k)
// 8. Compute e_jk = FS-HASH(g, q, y, p_i, j, k, m)
res, err := crypto.FiatShamir(params.Gx, params.Gy, params.N, y.X, y.Y, Pi, bij, bik, m)
if err != nil {
return nil, err
}
// 9. Set x_j = eJ1 || ... || eJs
// Pseudocode says to concatenate outside this loop
// however, we just concatenate the bytes now instead of storing as temporary
// variables
ej = append(ej, res...)
}
// 10. Truncate ej to b bits
xj := new(big.Int).SetBytes(ej[:b/8])
// 11. if x_j < Z_N* i.e. 0 < x_j and x_j < N
if xj.Cmp(crypto.Zero) == 1 && xj.Cmp(N) == -1 {
x[j] = xj
// 12 j = j + 1
j++
// 13 m = 0
m = big.NewInt(0)
// 14 else
} else {
// 15. Set m = m + 1
m.Add(m, crypto.One)
}
}
return x, nil
}
+394
View File
@@ -0,0 +1,394 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package paillier
import (
"crypto/elliptic"
"encoding/json"
"math/big"
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/stretchr/testify/require"
crypto "github.com/sonr-io/sonr/crypto/core"
curves2 "github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/internal"
)
var testPrimes = []*big.Int{
internal.B10(
"186141419611617071752010179586510154515933389116254425631491755419216243670159714804545944298892950871169229878325987039840135057969555324774918895952900547869933648175107076399993833724447909579697857041081987997463765989497319509683575289675966710007879762972723174353568113668226442698275449371212397561567",
),
internal.B10(
"94210786053667323206442523040419729883258172350738703980637961803118626748668924192069593010365236618255120977661397310932923345291377692570649198560048403943687994859423283474169530971418656709749020402756179383990602363122039939937953514870699284906666247063852187255623958659551404494107714695311474384687",
),
internal.B10(
"130291226847076770981564372061529572170236135412763130013877155698259035960569046218348763182598589633420963942796327547969527085797839549642610021986391589746295634536750785366034581957858065740296991986002552598751827526181747791647357767502200771965093659353354985289411489453223546075843993686648576029043",
),
internal.B10(
"172938910323633442195852028319756134734590277522945546987913328782597284762767185925315797321999389252040294991952361905020940252121762387957669654615602135429944435719699091344247805645764550860505536884031064967454028383404046221898300153428182409080298694828920944094158777327533157774919783417586902830043",
),
internal.B10(
"135841191929788643010555393808775051922265083622266098277752143441294911675705272940799534437169053045878247274810449617960047255023823301284034559807472662111224710158898548617194658983006262996831617082584649612602010680423107108651221824216065228161009680618243402116924511141821829055830713600437589058643",
),
internal.B10(
"179677777376220950493907657233669314916823596507009854134559513388779535023958212632715646194917807302098015450071151245496651913873851032302340489007561121851068326577148680474495447007833318066335149850926605897908761267606415610900931306044455332084757793630487163583451178807470499389106913845684353833379",
),
internal.B10(
"62649985409697862206708027961094957171873130708493280862148817115812710388878279240372941307490519941098268192630359164091992515623574326498710952492586770923230983753287493884398990474917756375654842939939940915963324175552421981212594421823752854754541693709434365609636589761589816398869727328798680335583",
),
internal.B10(
"196576931859098680370388202020086631604584490828609819764890020064880575503817891126703473215983239396058738287255240835101797315137072822716923594188151190460588551553676484461393180135097616711975997391550414447010491794087888246885960280296709672609456539741162207414899687167396008233995214434586323322859",
),
internal.B10(
"271336420864746369701165973306090650688066226258594853124089876839120277465060891854507381090238664515950686049792387028144049076707224579184820539700879884119579186284072404459682082855184644444282438298561112002507411996589407330801765394106772460665497195944412067027079123717579308322520985921886949051399",
),
internal.B10(
"147653127360336844448178027222853805809444645720500374788954343695331927468524513989671450440433430392339037667457657655958027740671071573403925974795764987870476118984896439440386146680643457835633462311776946902713168513155240275028008685964121441954481847113848701823211862974120297600518927026940189810103",
),
internal.B10(
"61539010433774119199101441060312213379096965116494840834113311373246794436251480454630309900106802555812462300777026043563820643373439814989443964169335227347638731691284339678436222951965582264570176875078394338903074717434072995072121221264531723385013005031614327462206339323414428493321384497439106152163",
),
internal.B10(
"311771090987243597109711542316907830756641693311804000593662622484722315782429237915515708860530841821213561483232298821623675096481796856960171671330638042763441430256097782130268494276848432981045602236986861083392706904041234926428759947857376161689191720483868111001987710383245853931937989224732484206639",
),
internal.B10(
"348545239501897032367950520763624245702184225360238826931782856428685149253861325854706825698843098817604431561258712026020688621010635185480321876001016614912927680387840531641703966894322797491484955817022624047355473480912508041252361257911175397626575812830091471419378132244146077774966527307225203863239",
),
internal.B10(
"167562983031509383478485987630533113343120902430985961468758712448125734458812918541051012669749885569679178971612428577288632429606851871845164719448590160530844833425628143996971699662729056519326776907622035340086832629206691942750594912221135787534670122007438859975313187460872690748138136170080913902203",
),
internal.B10(
"151715609132228776595716500435665208768897792993205818803431003524953811539898459230192282642811956896879836518212758893685104146944932088195466999437630114129887975508715417094019351746027667352287673763064246395392591213231796089814648654152625331299642171758052545451706130433176935280325874961374276589763",
),
internal.B10(
"281712498774102170277967098381151873986368736986748325672760355775943894718315925237789122870763991635031524237638254080973752419302437870447849091185409669906909828874532209010547976564209374430136988588219470076527433259993640332285914706329187116209118972038509278879237122949265824264856530096167843589043",
),
internal.B10(
"86882427063713590116032012033991745733440719961931885774819345297872782432110706546175706398857226544896860987721577779470479838062106788873559026307646871133570915480684987993282364698928926188640576189281202695899665555602891606955025957497645420156315890379148794822782242384167644977894987285630058930123",
),
internal.B10(
"83303406464212917441403726886711948278716398972782717472384580707071541544369912289531948826578557956123897261910116726555850408667234850301141318443154703778225305104540324875867615851047711871915209458107086347063530255813116254895432804373554367035028329996279513893337862177371103671113527972705508417219",
),
internal.B10(
"290829612093510969863838578444630131194824970528125429399090307997156531200557462531776190769158191441614466855963164356672434851525764502180873524299787560160992955274777477308009367164212073773611071813219472273292916120276721541591451163160398750751633065097434700462944540404208636130411713202759646572187",
),
internal.B10(
"48881643615473281990659967689574873112454227417010573158578046287415357392674453353386274403945930212163960526780980360358370255117866064326375672959460785974850231208253282115124348836379470403659096433419030132737534978624170609788431758477453270578400762995584298785082853409573009591146163658067217132999",
),
internal.B10(
"47818664065019136841944639898604168570191742429107462510943153778085167306149797958497736138014922106778177497683417470713996340979203175236987691632152128071256018368891463388744514315997309312144264057334961479235114340091423812710466596081259537323405527816634204224641066174554915788023121121924554823463",
),
internal.B10(
"229695065817346126758526483359337260282925372883465036895664320607569899180246065431164369223444026964554195802821338287119074827091354255558249504915527499047398698041873875019622138440848556549357174461846992529556315682586788137744896985847052668283284231628825924370859640129811142861116994552829398428647",
),
internal.B10(
"100501115016957347491693508757253443864758446032047524096020585354104983606736759639044367167134409576092528219013168507381959241052976704837620234061351712684500077849808223040972825725745511581504906610633548112115513604053190610668096089811015493012026572475283305559529666099836493252485826156659750294903",
),
internal.B10(
"164941338210872461448879529698900150056451305424623398039343691654768566536491493438826728710972441042226712374570117138883019423322602098706113298908005378303473844839971995715847918643285222217768344335264383514921181158565529236124115589719970140511822410990649549555047928093673140752570788415674868532299",
),
internal.B10(
"277037199251333188171108034082127252450960810846571117481542098050121457972964006392527344163695411986229107011168411232117984760439307440561490229321182283823299859836750264962218540927582605520434969388646847458788766216835350519741512353041653865564337457599778511921159510170311560556844284028160928114623",
),
internal.B10(
"194450633495795999995837828580097111229016136925345956148788562473415774074431957758705067019578300241653926511426134047652491974620284426575921711048247821582093564387999309993254763749991516361193554847964638949731507356038181346481870018202492098453036116472375750355687853028597643560794156133662678349939",
),
internal.B10(
"351287943170075259292767465687447861777186026969543801283411356809391771346213158326614410370157474105344692424346410925411240089238093158848815209147605381454294661108047095253146499393310219242924552687094316959878415907497273176683394122976929775532753961555304244867490393116346360677109958055297215711587",
),
internal.B10(
"329970582937843908299463472795994928703202360293919677760100619380642134736439176597341134222679061949935544692834943628667398294307004076774417457697130314341974849843695836050603685013468031012892094273233016929045028941716224648802422408386216033754532303003405690778077639489173685122357063674177077611499",
),
internal.B10(
"67751546105580063387575764356375682499165422473383503143930633584920975946879807021875353514236996076344028719391905234977223693643926924731304657199486141030504275775023186923364159168130612275534168246529309247449138607249407760246378829338068736888203134857601657561860157938495777271164458736576560502603",
),
internal.B10(
"276043923868350639738966705196129285523536747369492013710841410915407411458158268634302674024358477700030814139419613881758439643092328709376796555454484423864587139181985503560495790018232370315219082876142282958894264284344738294415199758590186234114829175455336589153989920707778566032047921277945163061363",
),
internal.B10(
"62028909880050184794454820320289487394141550306616974968340908736543032782344593292214952852576535830823991093496498970213686040280098908204236051130358424961175634703281821899530101130244725435470475135483879784963475148975313832483400747421265545413510460046067002322131902159892876739088034507063542087523",
),
internal.B10(
"321804071508183671133831207712462079740282619152225438240259877528712344129467977098976100894625335474509551113902455258582802291330071887726188174124352664849954838358973904505681968878957681630941310372231688127901147200937955329324769631743029415035218057960201863908173045670622969475867077447909836936523",
),
internal.B10(
"52495647838749571441531580865340679598533348873590977282663145916368795913408897399822291638579504238082829052094508345857857144973446573810004060341650816108578548997792700057865473467391946766537119012441105169305106247003867011741811274367120479722991749924616247396514197345075177297436299446651331187067",
),
internal.B10(
"118753381771703394804894143450628876988609300829627946826004421079000316402854210786451078221445575185505001470635997217855372731401976507648597119694813440063429052266569380936671291883364036649087788968029662592370202444662489071262833666489940296758935970249316300642591963940296755031586580445184253416139",
),
}
func TestGenerateChallengesWorks(t *testing.T) {
curves := []elliptic.Curve{btcec.S256(), elliptic.P256()}
for _, curve := range curves {
// dummy secret based on curve
y, _ := curves2.NewScalarBaseMult(curve, big.NewInt(3))
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
// Compute N from test primes
skN := new(big.Int).Mul(testPrimes[i], testPrimes[j])
x, err := generateChallenges(curve.Params(), skN, uint32(i+1), y)
require.NoError(t, err)
for _, xj := range x {
require.NotNil(t, xj)
require.Greater(t, xj.Cmp(crypto.Zero), 0)
require.Equal(t, xj.Cmp(skN), -1)
}
}
}
}
}
// tests whether the hash function can still produce valid challenges
// even when the modulus is smaller than the hash output
func TestGenerateChallengesPrimeN(t *testing.T) {
curves := []elliptic.Curve{btcec.S256(), elliptic.P256()}
n := []*big.Int{
internal.B10("680564733841876926926749214863536724007"),
internal.B10("1234345830644315716128223123383371693999"),
internal.B10(
"358070498390900219760227294443948492156530525739357363711230524749453568134007",
),
internal.B10(
"409819537231165473932776844223512813127760876374228481246566335070809195677439",
),
internal.B10(
"336327054820888403283842064345570507895192245801107954728200717775133442039527",
),
internal.B10(
"353526063197730551176241127281213353808518592628930654494044427064787696719527",
),
}
for _, curve := range curves {
// dummy secret based on curve
y, _ := curves2.NewScalarBaseMult(curve, big.NewInt(3))
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
x, err := generateChallenges(curve.Params(), n[i], uint32(j+1), y)
require.NoError(t, err)
for _, xj := range x {
require.NotNil(t, xj)
require.Greater(t, xj.Cmp(crypto.Zero), 0)
}
}
}
}
}
func TestGenerateChallengesNilInputs(t *testing.T) {
y, _ := curves2.NewScalarBaseMult(elliptic.P256(), big.NewInt(1))
_, err := generateChallenges(nil, nil, 0, nil)
require.Error(t, err)
_, err = generateChallenges(elliptic.P256().Params(), nil, 0, nil)
require.Error(t, err)
_, err = generateChallenges(elliptic.P256().Params(), big.NewInt(0), 0, nil)
require.Error(t, err)
_, err = generateChallenges(elliptic.P256().Params(), big.NewInt(1), 1, nil)
require.Error(t, err)
_, err = generateChallenges(elliptic.P256().Params(), big.NewInt(1), 1, y)
require.Error(t, err)
_, err = generateChallenges(elliptic.P256().Params(), big.NewInt(1), 1, y)
require.Error(t, err)
_, err = generateChallenges(elliptic.P256().Params(), big.NewInt(255), 1, y)
require.NoError(t, err)
}
func TestPsfProofParams_Prove(t *testing.T) {
// RSA primes for testing
sk, _ := NewSecretKey(
// 75-digit random primes from Wolfram-Alpha
internal.B10(
"110045198697022997120409435651962875820936327127306040565577217116705932648687",
),
internal.B10(
"95848033199746534486927143950536999279071340697368502822602282152563330640779",
),
)
smallSk, _ := NewSecretKey(big.NewInt(13), big.NewInt(11))
// Some points for testing
k := internal.B10("270988338908697209412444309907441365656383309727758604622908325428179708750")
Qp256, _ := curves2.NewScalarBaseMult(elliptic.P256(), k)
Qs256, _ := curves2.NewScalarBaseMult(btcec.S256(), k)
pi := uint32(4)
tests := []struct {
name string
in *PsfProofParams
expectedError error
expectedResultLen int
}{
// Positive tests
{
"positive: p256, small numbers",
&PsfProofParams{elliptic.P256(), smallSk, 1001, Qp256},
nil,
PsfProofLength,
},
{
"positive: p256, large numbers",
&PsfProofParams{elliptic.P256(), sk, pi, Qp256},
nil,
PsfProofLength,
},
{
"positive: s256, large numbers",
&PsfProofParams{btcec.S256(), sk, pi, Qs256},
nil,
PsfProofLength,
},
// Nil params
{
"negative: ",
&PsfProofParams{
btcec.S256(),
sk,
pi,
Qs256,
},
nil,
PsfProofLength,
},
{
"negative: proof params are nil",
&PsfProofParams{
nil,
sk,
pi,
Qs256,
},
internal.ErrNilArguments,
0,
},
{
"negative: SecretKey is nil",
&PsfProofParams{
btcec.S256(),
nil,
pi,
Qs256,
},
internal.ErrNilArguments,
0,
},
{
"negative: y is nil",
&PsfProofParams{
btcec.S256(),
sk,
0,
Qs256,
},
internal.ErrNilArguments,
0,
},
{
"negative: Pi is nil",
&PsfProofParams{
btcec.S256(),
sk,
pi,
nil,
},
internal.ErrNilArguments,
0,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// test
result, err := test.in.Prove()
// Verify the results are as expected
require.Equal(t, test.expectedError, err)
require.Len(t, result, test.expectedResultLen)
})
}
}
// Test that Range2Proof can be marshaled and unmarshaled correctly
func TestPsfProof_MarshalJSON(t *testing.T) {
// Generate some proof
sk, _ := NewSecretKey(
internal.B10(
"110045198697022997120409435651962875820936327127306040565577217116705932648687",
),
internal.B10(
"95848033199746534486927143950536999279071340697368502822602282152563330640779",
),
)
Q, _ := curves2.NewScalarBaseMult(elliptic.P256(),
internal.B10("270988338908697209412444309907441365656383309727758604622908325428179708750"))
params := &PsfProofParams{
elliptic.P256(), sk,
1,
Q,
}
proof, err := params.Prove()
require.NoError(t, err)
require.NotNil(t, proof)
// Marshal
testJSON, err := json.Marshal(proof)
require.NoError(t, err)
require.NotNil(t, testJSON)
var unmarshaled PsfProof
err = json.Unmarshal(testJSON, &unmarshaled)
require.NoError(t, err)
// Test for equality
require.Len(t, ([]*big.Int)(unmarshaled), len(([]*big.Int)(proof)))
for i := range proof {
require.Equal(t, proof[i], unmarshaled[i])
}
}
// Tests for Verify
// prove/verify round trip works
// modifying any parameter causes verify to fail
func TestPsfProofWorks(t *testing.T) {
// RSA primes for testing
sk, _ := NewSecretKey(
// 75-digit random primes from Wolfram-Alpha
internal.B10(
"110045198697022997120409435651962875820936327127306040565577217116705932648687",
),
internal.B10(
"95848033199746534486927143950536999279071340697368502822602282152563330640779",
),
)
// Some points for testing
k := internal.B10("270988338908697209412444309907441365656383309727758604622908325428179708750")
Qp256, _ := curves2.NewScalarBaseMult(elliptic.P256(), k)
pi := uint32(2)
proveParams := &PsfProofParams{
Curve: elliptic.P256(),
SecretKey: sk,
Pi: pi,
Y: Qp256,
}
proof, _ := proveParams.Prove()
verifyParams := &PsfVerifyParams{
Curve: elliptic.P256(),
PublicKey: &sk.PublicKey,
Pi: pi,
Y: Qp256,
}
require.NoError(t, proof.Verify(verifyParams))
}