sonr/crypto/core/mod_test.go
Prad Nukala 31bcc21c35
feature/1121 implement ucan validation (#1176)
- **refactor: remove unused auth components**
- **refactor: improve devbox configuration and deployment process**
- **refactor: improve devnet and testnet setup**
- **fix: update templ version to v0.2.778**
- **refactor: rename pkl/net.matrix to pkl/matrix.net**
- **refactor: migrate webapp components to nebula**
- **refactor: protobuf types**
- **chore: update dependencies for improved security and stability**
- **feat: implement landing page and vault gateway servers**
- **refactor: Migrate data models to new module structure and update
related files**
- **feature/1121-implement-ucan-validation**
- **refactor: Replace hardcoded constants with model types in attns.go**
- **feature/1121-implement-ucan-validation**
- **chore: add origin Host struct and update main function to handle
multiple hosts**
- **build: remove unused static files from dwn module**
- **build: remove unused static files from dwn module**
- **refactor: Move DWN models to common package**
- **refactor: move models to pkg/common**
- **refactor: move vault web app assets to embed module**
- **refactor: update session middleware import path**
- **chore: configure port labels and auto-forwarding behavior**
- **feat: enhance devcontainer configuration**
- **feat: Add UCAN middleware for Echo with flexible token validation**
- **feat: add JWT middleware for UCAN authentication**
- **refactor: update package URI and versioning in PklProject files**
- **fix: correct sonr.pkl import path**
- **refactor: move JWT related code to auth package**
- **feat: introduce vault configuration retrieval and management**
- **refactor: Move vault components to gateway module and update file
paths**
- **refactor: remove Dexie and SQLite database implementations**
- **feat: enhance frontend with PWA features and WASM integration**
- **feat: add Devbox features and streamline Dockerfile**
- **chore: update dependencies to include TigerBeetle**
- **chore(deps): update go version to 1.23**
- **feat: enhance devnet setup with PATH environment variable and
updated PWA manifest**
- **fix: upgrade tigerbeetle-go dependency and remove indirect
dependency**
- **feat: add PostgreSQL support to devnet and testnet deployments**
- **refactor: rename keyshare cookie to token cookie**
- **feat: upgrade Go version to 1.23.3 and update dependencies**
- **refactor: update devnet and testnet configurations**
- **feat: add IPFS configuration for devnet**
- **I'll help you update the ipfs.config.pkl to include all the peers
from the shell script. Here's the updated configuration:**
- **refactor: move mpc package to crypto directory**
- **feat: add BIP32 support for various cryptocurrencies**
- **feat: enhance ATN.pkl with additional capabilities**
- **refactor: simplify smart account and vault attenuation creation**
- **feat: add new capabilities to the Attenuation type**
- **refactor: Rename MPC files for clarity and consistency**
- **feat: add DIDKey support for cryptographic operations**
- **feat: add devnet and testnet deployment configurations**
- **fix: correct key derivation in bip32 package**
- **refactor: rename crypto/bip32 package to crypto/accaddr**
- **fix: remove duplicate indirect dependency**
- **refactor: move vault package to root directory**
- **refactor: update routes for gateway and vault**
- **refactor: remove obsolete web configuration file**
- **refactor: remove unused TigerBeetle imports and update host
configuration**
- **refactor: adjust styles directory path**
- **feat: add broadcastTx and simulateTx functions to gateway**
- **feat: add PinVault handler**
2024-12-02 14:27:18 -05:00

566 lines
16 KiB
Go
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package core
import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/onsonr/sonr/crypto/internal"
)
var (
four = big.NewInt(4)
// Large numbers for testing -- computing with independent tooling
// x,y 100-digit numbers
x, _ = new(big.Int).SetString("7146643783615963513942641287213372249533955323510461217840179896547799100626220786140425637990097431", 10)
y, _ = new(big.Int).SetString("1747698065194620177681258504464368264357359841192790848951902311522815739310792522712583635858354245", 10)
sumxy, _ = new(big.Int).SetString("8894341848810583691623899791677740513891315164703252066792082208070614839937013308853009273848451676", 10)
xy, _ = new(big.Int).SetString("12490175513260779219420155073726764321605372267033815716483640700978475653623775696463227582174703069158832890348206546318843052423532258178885792744599932977235221784868792263260215861775082862444595", 10)
// 101-digit modulus
m, _ = new(big.Int).SetString("85832751158419329546684678412285185885848111422509523329716452068504806021136687603399722116388773253", 10)
// 99-digit modulus
n, _ = new(big.Int).SetString("604464499356780653111583485887412477603580949137220100557796699530113283915988830359783807274682723", 10)
)
func TestConstantTimeEqByteSound(t *testing.T) {
hundoDigit := internal.B10("3593421565679030456559622742114065111786271367498220644136232358421457354322411370928949366452183472")
tests := []struct {
name string
a, b *big.Int
expected byte
}{
{"positive: 5", internal.B10("5"), internal.B10("5"), 1},
{"positive: 100", internal.B10("100"), internal.B10("100"), 1},
{"positive: -1204", internal.B10("-1204"), internal.B10("-1204"), 1},
{"positive: 100 digits", hundoDigit, hundoDigit, 1},
{"positive: 0", internal.B10("0"), internal.B10("0"), 1},
{"positive: 0/-0", internal.B10("0"), internal.B10("-0"), 1},
{"positive: -0/-0", internal.B10("-0"), internal.B10("-0"), 1},
{"negative: 5/-5", internal.B10("5"), internal.B10("-5"), 0},
{"negative: 5/500", internal.B10("5"), internal.B10("500"), 0},
{"negative: 100/100 digit", internal.B10("100"), hundoDigit, 0},
{"negative: -1204/-5", internal.B10("-1204"), internal.B10("-15"), 0},
{"negative: 0/-5 digits", internal.B10("0"), internal.B10("-5"), 0},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := ConstantTimeEqByte(test.a, test.b)
require.Equal(t, test.expected, actual)
})
}
}
func TestConstantTimeEqSound(t *testing.T) {
hundoDigit := internal.B10("3593421565679030456559622742114065111786271367498220644136232358421457354322411370928949366452183472")
tests := []struct {
name string
a, b *big.Int
expected bool
}{
{"positive: 5", internal.B10("5"), internal.B10("5"), true},
{"positive: 100", internal.B10("100"), internal.B10("100"), true},
{"positive: -1204", internal.B10("-1204"), internal.B10("-1204"), true},
{"positive: 100 digits", hundoDigit, hundoDigit, true},
{"positive: 0", internal.B10("0"), internal.B10("0"), true},
{"positive: 0/-0", internal.B10("0"), internal.B10("-0"), true},
{"positive: -0/-0", internal.B10("-0"), internal.B10("-0"), true},
{"negative: 5/-5", internal.B10("5"), internal.B10("-5"), false},
{"negative: 5/500", internal.B10("5"), internal.B10("500"), false},
{"negative: 100/100 digit", internal.B10("100"), hundoDigit, false},
{"negative: -1204/-5", internal.B10("-1204"), internal.B10("-15"), false},
{"negative: 0/-5 digits", internal.B10("0"), internal.B10("-5"), false},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := ConstantTimeEq(test.a, test.b)
require.Equal(t, test.expected, actual)
})
}
}
// Ring membership tests
func TestIn(t *testing.T) {
// Some large numbers for testing
x, _ := new(big.Int).SetString("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10)
y, _ := new(big.Int).SetString("32168432167132168106409840321684604654063138460840123871234181628904319728058", 10)
N := new(big.Int).Mul(x, y) // N = xy
NN := new(big.Int).Mul(N, N) // N^2 = N*N = x^2y^2
errMember := internal.ErrZmMembership
tests := []struct {
x *big.Int
m *big.Int
expected error
}{
//
// Completist test for: Z_4
//
// Too small: -x ∉ Z_4, \forall \x \in \N
{big.NewInt(-4), four, errMember},
{big.NewInt(-3), four, errMember},
{big.NewInt(-2), four, errMember},
{big.NewInt(-1), four, errMember},
// Just right: {0,1,2,3} = Z_4
{big.NewInt(0), four, nil},
{big.NewInt(1), four, nil},
{big.NewInt(2), four, nil},
{big.NewInt(3), four, nil},
// Too big: {4,5,6,7} ∉ Z_4
{big.NewInt(4), four, errMember},
{big.NewInt(5), four, errMember},
{big.NewInt(6), four, errMember},
{big.NewInt(7), four, errMember},
//
// Large numbers
//
// x,y,N < N^2
{x, NN, nil},
{y, NN, nil},
{N, NN, nil},
// N+x,N+y,2N < N^2 ⇒ x ∈ Z_N^2
{big.NewInt(0).Add(N, x), NN, nil},
{big.NewInt(0).Add(N, y), NN, nil},
{big.NewInt(0).Add(N, N), NN, nil},
// Nx,Ny < N^2 ⇒ x ∈ Z_N^2
{big.NewInt(0).Mul(N, x), NN, nil},
{big.NewInt(0).Mul(N, y), NN, nil},
// -x,-y,-N ∉ Z_N^2
{big.NewInt(0).Neg(x), NN, errMember},
{big.NewInt(0).Neg(y), NN, errMember},
{big.NewInt(0).Neg(N), NN, errMember},
// N^2 ∉ Z_N^2
{NN, NN, errMember},
}
// All the tests!
for _, test := range tests {
actual := In(test.x, test.m)
require.Equal(t, test.expected, actual)
}
}
// Tests for modular addition with known answers
func TestAdd(t *testing.T) {
// Pre-compute some values
sumXyModn, err := Add(x, y, n)
require.Nil(t, err)
tests := []struct {
x, y, m, expected *big.Int // inputs: x,y,m
}{
// Small number tests
{big.NewInt(-1), big.NewInt(1), four, big.NewInt(0)},
{big.NewInt(2), big.NewInt(1), four, big.NewInt(3)},
{big.NewInt(0), big.NewInt(2), four, big.NewInt(2)},
{big.NewInt(2), big.NewInt(4), four, big.NewInt(2)},
{big.NewInt(15), big.NewInt(15), four, big.NewInt(2)},
// Large number tests
{x, y, m, sumxy},
{y, x, m, sumxy},
// Large number Zero tests
{Zero, x, m, x},
{x, Zero, m, x},
{Zero, y, m, y},
{y, Zero, m, y},
// Commutative
{x, y, m, sumxy},
{y, x, m, sumxy},
{x, y, n, sumXyModn},
{y, x, n, sumXyModn},
{sumXyModn, Zero, n, sumXyModn},
{Zero, sumXyModn, n, sumXyModn},
}
// All the tests!
for _, test := range tests {
actual, err := Add(test.x, test.y, test.m)
require.NoError(t, err)
require.Zero(t, actual.Cmp(test.expected))
}
}
// Tests for modular addition according to known invariants
func TestAddInvariants(t *testing.T) {
inputs := []*big.Int{x, y, Zero, One, new(big.Int).Neg(x), new(big.Int).Neg(y)}
moduli := []*big.Int{m, n, big.NewInt(10001)}
// Run all combinations of the inputs/moduli
for _, x := range inputs {
for _, y := range inputs {
for _, m := range moduli {
// Addition is commutative
z0, err := Add(x, y, m)
require.NoError(t, err)
z1, err := Add(y, x, m)
require.NoError(t, err)
require.Equal(t, z0, z1)
// Addition is transitive: x+x+y == y+x+x == x+y+x
a0, _ := Add(x, x, m)
a0, _ = Add(a0, y, m)
a1, _ := Add(y, x, m)
a1, _ = Add(a1, x, m)
a2, _ := Add(x, y, m)
a2, _ = Add(a2, x, m)
require.Equal(t, a0, a1)
require.Equal(t, a1, a2)
}
}
}
}
// Tests modular multiplication with known answers
func TestMul(t *testing.T) {
// Pre-compute some values
xyModm := new(big.Int).Mod(xy, m)
tests := []struct {
x, y, m, expected *big.Int // inputs: x,y,m
}{
// Small number tests
{big.NewInt(-1), big.NewInt(1), four, big.NewInt(3)},
{big.NewInt(2), big.NewInt(1), four, big.NewInt(2)},
{big.NewInt(0), big.NewInt(2), four, big.NewInt(0)},
{big.NewInt(2), big.NewInt(4), four, big.NewInt(0)},
{big.NewInt(15), big.NewInt(15), four, big.NewInt(1)},
// Large number tests
{x, y, m, xyModm},
{y, x, m, xyModm},
// Large number Zero tests
{Zero, x, m, Zero},
{x, Zero, m, Zero},
{Zero, y, n, Zero},
}
// All the tests!
for _, test := range tests {
z, err := Mul(test.x, test.y, test.m)
require.NoError(t, err)
require.Zero(t, z.Cmp(test.expected))
}
}
// Tests for modular multiplication according to known invariants
func TestMulInvariants(t *testing.T) {
inputs := []*big.Int{x, y, Zero, One, new(big.Int).Neg(x), new(big.Int).Neg(y)}
moduli := []*big.Int{m, n, big.NewInt(10001)}
// Run all combinations of the inputs/moduli
for _, x := range inputs {
for _, y := range inputs {
for _, m := range moduli {
// Mul is commutative
a, err := Mul(x, y, m)
require.NoError(t, err)
aʹ, err := Mul(y, x, m)
require.NoError(t, err)
require.Equal(t, a, aʹ)
// Mul is transitive: (xx)y == (xy)x
z, _ := Mul(x, x, m)
z, _ = Mul(z, y, m)
zʹ, _ := Mul(x, y, m)
zʹ, _ = Mul(zʹ, x, m)
require.Equal(t, z, zʹ)
}
}
}
}
// Tests modular negation with known answers
func TestNeg(t *testing.T) {
tests := []struct {
x, m, e *big.Int
}{
{big.NewInt(1), big.NewInt(7), big.NewInt(6)},
{big.NewInt(2), big.NewInt(7), big.NewInt(5)},
{big.NewInt(3), big.NewInt(7), big.NewInt(4)},
{big.NewInt(4), big.NewInt(7), big.NewInt(3)},
{big.NewInt(5), big.NewInt(7), big.NewInt(2)},
{big.NewInt(6), big.NewInt(7), big.NewInt(1)},
{big.NewInt(-1), big.NewInt(7), big.NewInt(1)},
{big.NewInt(-2), big.NewInt(7), big.NewInt(2)},
{big.NewInt(-3), big.NewInt(7), big.NewInt(3)},
{big.NewInt(-4), big.NewInt(7), big.NewInt(4)},
{big.NewInt(-5), big.NewInt(7), big.NewInt(5)},
{big.NewInt(-6), big.NewInt(7), big.NewInt(6)},
{big.NewInt(8), big.NewInt(7), big.NewInt(6)},
{big.NewInt(9), big.NewInt(7), big.NewInt(5)},
{big.NewInt(10), big.NewInt(7), big.NewInt(4)},
{big.NewInt(11), big.NewInt(7), big.NewInt(3)},
{big.NewInt(12), big.NewInt(7), big.NewInt(2)},
{big.NewInt(13), big.NewInt(7), big.NewInt(1)},
}
for _, test := range tests {
r, err := Neg(test.x, test.m)
require.NoError(t, err)
if r.Cmp(test.e) != 0 {
t.Errorf("TestNeg failed. Expected %v, got: %v ", test.e, r)
}
}
}
func TestNegInvariants(t *testing.T) {
tests := []struct {
x, m, e *big.Int
}{
{big.NewInt(0), big.NewInt(7), big.NewInt(0)},
{big.NewInt(7), big.NewInt(7), big.NewInt(0)},
{big.NewInt(-7), big.NewInt(7), big.NewInt(0)},
}
for _, test := range tests {
r, err := Neg(test.x, test.m)
require.NoError(t, err)
if r.Cmp(test.e) != 0 {
t.Errorf("TestNeg failed. Expected %v, got: %v ", test.e, r)
}
}
}
// Simple test for distinct Rand output
func TestRandDistinct(t *testing.T) {
// Each value should be distinct
a, _ := Rand(n)
b, _ := Rand(n)
c, _ := Rand(n)
// ❄️❄️❄️
require.NotEqual(t, a, b)
require.NotEqual(t, a, c)
require.NotEqual(t, b, c)
}
// Rand values should be O(log2(m)) bits
func TestRandIsExpectedLength(t *testing.T) {
trials := 1000
max := big.NewInt(-1)
// Generate many nonces, keep the max
for i := 0; i < trials; i++ {
r, err := Rand(m)
require.NoError(t, err)
// Nonces should be < m
if r.Cmp(m) != -1 {
t.Errorf("nonce too large, require %v < %v", r, m)
}
if r.Cmp(max) == 1 {
max = r
}
}
// With high probability, the max nonce should be very close N
lowerBound := new(big.Int).Rsh(m, 1)
if max.Cmp(lowerBound) == -1 {
t.Errorf("Expected max nonce: %v > %v", max, lowerBound)
}
}
// Randomly selected nonces with a large modulus will be unique with overwhelming probability
func TestRandDistinctWithLargeModulus(t *testing.T) {
const iterations = 1000
testUnique(t, iterations, func() *big.Int {
r, _ := Rand(m)
return r
})
}
// Calls sampleFunc() n times and asserts that the lower 64B of each output are unique.
func testUnique(t *testing.T, iterations int, sampleFunc func() *big.Int) {
// For simplicity, we test only the lower 64B of each nonce. This is sufficient
// to prove uniqueness and go-lang doesn't hash slices (no slices in maps)
const size = 256 / 8
seen := make(map[[size]byte]bool)
var x [size]byte
// Check the pre-computed commitments for uniquness
for i := 0; i < iterations; i++ {
// Retrieve a sample
sample := sampleFunc()
require.NotNil(t, sample)
// Copy the bytes from slice>array
copy(x[:], sample.Bytes())
// Ensure each sample is unique
if seen[x] {
t.Errorf("duplicate sample found: %v", x)
}
seen[x] = true
}
}
// Ensure Rand never returns 0 or 1.
func TestRandNotZeroNotOne(t *testing.T) {
// Test for non-zero only useful when iterations >> |Z_m|
const iterations = 1000
m := big.NewInt(5)
for i := 0; i < iterations; i++ {
r, err := Rand(m)
require.NoError(t, err)
// Not 0 or 1
require.NotEqual(t, r, Zero)
require.NotEqual(t, r, One)
}
}
func TestRand_NilModulusErrors(t *testing.T) {
r, err := Rand(nil)
require.Nil(t, r)
require.Contains(t, err.Error(), internal.ErrNilArguments.Error())
}
// Double-inverse is the identity function in fields
func TestInvRoundTrip(t *testing.T) {
m := internal.B10("1031") // Prime-order modulus
for _, a := range []*big.Int{
internal.B10("500"),
internal.B10("-500"),
internal.B10("1"),
internal.B10("1030"),
} {
// Our expected value is the modular reduction of the test value
expected := a.Mod(a, m)
// Invert and check
aInv, err := Inv(a, m)
require.NoError(t, err, "a=%v", a)
require.NotNil(t, aInv)
// Invert again and check
a_, err := Inv(aInv, m)
if err != nil {
require.Equal(t, expected, a_)
}
}
}
// Tests values for which there is no inverse in the given field
func TestInvNotFound(t *testing.T) {
m := internal.B10("1024") // m = 2^10
// 0 and even numbers will not have inverse in this ring
for _, a := range []*big.Int{
internal.B10("500"),
internal.B10("-500"),
internal.B10("0"),
internal.B10("1024"),
internal.B10("512"),
internal.B10("300000000"),
} {
// Invert and check
aInv, err := Inv(a, m)
require.Error(t, err, "a=%v", a)
require.Nil(t, aInv)
}
}
func TestExpKnownAnswer(t *testing.T) {
p := internal.B10("1031") // prime-order field
pMinus1 := internal.B10("1030")
tests := []struct {
name string
x, e, m *big.Int
expected *big.Int
}{
{"fermat's little thm: 500", internal.B10("500"), p, p, internal.B10("500")},
{"fermat's little thm (p-1): 500", internal.B10("500"), pMinus1, p, One},
{"fermat's little thm (p-1): 5000", internal.B10("5000"), pMinus1, p, One},
{"399^0 = 1", internal.B10("399"), Zero, p, One},
{"673^1 = 673", internal.B10("673"), One, p, internal.B10("673")},
}
// Run all the tests!
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := Exp(test.x, test.e, test.m)
if err != nil {
require.Equal(t, test.expected, actual)
}
})
}
}
// A product of two 1024b safe primes
var N1024 = internal.B10("22657252520748253292205422817162431301953923432914829530688424232913850279325496327198502914522231560238552529734156383924448818535517634061008476071362010781638360092704508943571866960229942049437914690556866055765377519627454975682400206932320319743805083072214857842762721537739950074695623974079312071498296625705376593890814889314744719469735809152488403143751157723139035869185892099006348653635981206799193781030834368833947197930944812082594326193527332208252230115672713914945889734620959932802893197325106135662762752470236627025599443912886530954179753873735786171937758916890000958846322096261981191349917")
// A product of two 256b safe primes
var N256 = internal.B10("10815068324662993508164204692909269429257853772524581783499643160896147777579932560873002543907262462663453338979819981987639157192530671167315407970757417")
func Benchmark_rand1024(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N1024) // nolint
}
}
func BenchmarkRand1024(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N1024) // nolint
}
}
func Benchmark_rand256(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N256) // nolint
}
}
func BenchmarkRandStar256(b *testing.B) {
if testing.Short() {
b.Skip("skipping test in short mode.")
}
for i := 0; i < b.N; i++ {
Rand(N256) // nolint
}
}