feat: secret stuff

This commit is contained in:
Vlad Stan 2022-10-10 22:56:03 +03:00
parent 8af4f95cc1
commit 422711649f
5 changed files with 146 additions and 59 deletions

View File

@ -6,7 +6,7 @@ Alice:
A = a*G
return A
Bob:
Y = hash_to_point(secret_message)
Y = hash_to_curve(secret_message)
r = random blinding factor
B'= Y + r*G
return B'
@ -20,7 +20,7 @@ C = C' - r*A
(= a*Y)
return C, secret_message
Alice:
Y = hash_to_point(secret_message)
Y = hash_to_curve(secret_message)
C == a*Y
If true, C must have originated from Alice
"""
@ -30,28 +30,23 @@ import hashlib
from secp256k1 import PrivateKey, PublicKey
def hash_to_point(secret_msg):
"""Generates x coordinate from the message hash and checks if the point lies on the curve.
def hash_to_curve(message: bytes):
"""Generates a point from the message hash and checks if the point lies on the curve.
If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
point = None
msg = secret_msg
msg_to_hash = message
while point is None:
_hash = hashlib.sha256(msg).hexdigest().encode("utf-8")
try:
# We construct compressed pub which has x coordinate encoded with even y
_hash = list(_hash[:33]) # take the 33 bytes and get a list of bytes
_hash[0] = 0x02 # set first byte to represent even y coord
_hash = bytes(_hash)
point = PublicKey(_hash, raw=True)
_hash = hashlib.sha256(msg_to_hash).digest()
point = PublicKey(b"\x02" + _hash, raw=True)
except:
msg = _hash
msg_to_hash = _hash
return point
def step1_alice(secret_msg):
secret_msg = secret_msg.encode("utf-8")
Y = hash_to_point(secret_msg)
secret_msg = secret_msg
Y = hash_to_curve(secret_msg)
r = PrivateKey()
B_ = Y + r.pubkey
return B_, r
@ -68,7 +63,7 @@ def step3_alice(C_, r, A):
def verify(a, C, secret_msg):
Y = hash_to_point(secret_msg.encode("utf-8"))
Y = hash_to_curve(secret_msg)
return C == Y.mult(a)

View File

@ -1,4 +1,5 @@
import hashlib
import base64
from typing import List, Set
from .core.b_dhke import verify
@ -32,14 +33,16 @@ def derive_pubkeys(keys: List[PrivateKey]):
# async required?
async def verify_proof(master_prvkey: str, proofs_used: Set[str], proof: Proof):
"""Verifies that the proof of promise was issued by this ledger."""
if proof.secret in proofs_used:
raise Exception(f"tokens already spent. Secret: {proof.secret}")
# if proof.secret in proofs_used:
# raise Exception(f"tokens already spent. Secret: {proof.secret}")
secret_key = derive_keys(master_prvkey)[
proof.amount
] # Get the correct key to check against
C = PublicKey(bytes.fromhex(proof.C), raw=True)
validMintSig = verify(secret_key, C, proof.secret)
secret = base64.urlsafe_b64decode(proof.secret)
print('### secret', secret)
validMintSig = verify(secret_key, C, secret)
if validMintSig != True:
raise Exception(f"tokens not valid. Secret: {proof.secret}")

View File

@ -0,0 +1,40 @@
function unescapeBase64Url (str) {
return (str + '==='.slice((str.length + 3) % 4))
.replace(/-/g, '+')
.replace(/_/g, '/')
}
function escapeBase64Url (str) {
return str.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '')
}
const uint8ToBase64 = (function (exports) {
'use strict';
var fromCharCode = String.fromCharCode;
var encode = function encode(uint8array) {
var output = [];
for (var i = 0, length = uint8array.length; i < length; i++) {
output.push(fromCharCode(uint8array[i]));
}
return btoa(output.join(''));
};
var asCharCode = function asCharCode(c) {
return c.charCodeAt(0);
};
var decode = function decode(chars) {
return Uint8Array.from(atob(chars), asCharCode);
};
exports.decode = decode;
exports.encode = encode;
return exports;
}({}));

View File

@ -1,15 +1,19 @@
async function hashToCurve(secretMessage) {
console.log(
'### secretMessage',
nobleSecp256k1.utils.bytesToHex(secretMessage)
)
let point
while (!point) {
const hash = await nobleSecp256k1.utils.sha256(secretMessage)
const hashHex = nobleSecp256k1.utils.bytesToHex(hash)
const pointX = '02' + hashHex
console.log('### pointX', pointX)
try {
point = nobleSecp256k1.Point.fromHex(hash)
point = nobleSecp256k1.Point.fromHex(pointX)
console.log('### point', point.toHex())
} catch (error) {
// console.error(error)
// const x = bytesToNumber(hash) + ''
// const msg = await nobleSecp256k1.utils.sha256(x)
secretMessage = await nobleSecp256k1.utils.sha256(hash)
// secretMessage = nobleSecp256k1.utils.bytesToHex(msg)
secretMessage = await nobleSecp256k1.utils.sha256(secretMessage)
}
}
return point
@ -26,6 +30,7 @@ async function step1Bob(secretMessage) {
}
function step3Bob(C_, r, A) {
const C = C_.subtract(A.multiply(r))
const rInt = BigInt(r)
const C = C_.subtract(A.multiply(rInt))
return C
}

View File

@ -240,7 +240,8 @@ page_container %}
</div>
<div v-else class="q-mb-lg">
{% raw %}
<strong>Amount:</strong> {{ sellData.invoice.sat }} <strong>sats</strong><br />
<strong>Amount:</strong> {{ sellData.invoice.sat }}
<strong>sats</strong><br />
<strong>Description:</strong> {{ sellData.invoice.description }}<br />
<strong>Expire date:</strong> {{ sellData.invoice.expireDate }}<br />
<strong>Expired:</strong> {{ sellData.invoice.expired }}<br />
@ -315,6 +316,7 @@ page_container %}
mintId: '',
mintName: '',
keys: '',
buyOrders: [],
buyData: {
amount: 0,
@ -782,9 +784,14 @@ page_container %}
const secrets = []
const randomBlindingFactors = []
for (let i = 0; i < amounts.length; i++) {
// const secret = bytesToNumber(nobleSecp256k1.utils.randomBytes(32)) + ''
const secret = nobleSecp256k1.utils.randomBytes(32)
secrets.push(secret)
// const secret = nobleSecp256k1.utils.randomBytes(32)
const secret = nobleSecp256k1.utils.hexToBytes('0000000000000000000000000000000000000000000000000000000000000003')
const encodedSecret = uint8ToBase64.encode(secret)
console.log('### encodedSecret', encodedSecret)
const decodedSecret = uint8ToBase64.decode(encodedSecret)
const hexSecret = nobleSecp256k1.utils.bytesToHex(decodedSecret)
console.log('### decodedSecret', hexSecret)
secrets.push(encodedSecret)
const {B_, randomBlindingFactor} = await step1Bob(secret)
randomBlindingFactors.push(randomBlindingFactor)
blindedMessages.push({amount: amounts[i], B_: B_})
@ -798,28 +805,6 @@ page_container %}
status: 'pending'
}
return newTokens
// console.log('### payloadsJson.payloads', payloadsJson.payloads)
// const promises = await mintApi.mint(payloadsJson.payloads, paymentHash)
// if (promises.error) {
// throw new Error(promises.error)
// }
// return this._constructProofs(promises, randomBlindingFactors, secrets)
},
_constructProofs: function (promises, randomBlindingFactors, secrets) {
return promises.map((p, i) => {
const C_ = nobleSecp256k1.Point.fromHex(p['C_'])
const A = this.keys[p.amount]
const C = step3Bob(
C_,
randomBlindingFactors[i],
nobleSecp256k1.Point.fromHex(A)
).toHex()
return {
amount: p.amount,
C: {C, secret: secrets[i]}
}
})
},
checkInvoice: function () {
@ -869,6 +854,57 @@ page_container %}
sellTokens: async function () {
console.log('#### sell tokens')
const amount = this.sellData.invoice.sat
const token = this.tokens
.filter(t => t.promises?.length)
.find(t => t.promises.find(b => b.amount === amount))
console.log('### token', token)
if (token) {
const promiseIndex = token.promises
.map(p => `${p.amount}`)
.indexOf(`${amount}`)
const promise = token.promises[promiseIndex]
console.log('### promise', promise)
const secret = token.secrets[promiseIndex]
const randomBlindingFactor = token.randomBlindingFactors[promiseIndex]
const C_ = nobleSecp256k1.Point.fromHex(promise['C_'])
const A = this.keys[promise.amount] // todo
console.log('#### C_', C_)
console.log('#### A', A)
const C = step3Bob(
C_,
randomBlindingFactor,
nobleSecp256k1.Point.fromHex(A)
)
const proofs = [
{
amount,
secret,
C: C.toHex(true)
}
]
const payload = {
proofs,
amount,
invoice: this.sellData.bolt11
}
console.log('#### payload', JSON.stringify(payload))
}
},
fetchMintKeys: async function () {
const {data} = await LNbits.api.request(
'GET',
`/cashu/api/v1/cashu/${this.mintId}/keys`
)
this.keys = data
localStorage.setItem('cashu.keys', JSON.stringify(data))
},
storeBuyOrders: function () {
@ -929,6 +965,13 @@ page_container %}
this.mintName = this.$q.localStorage.getItem('cashu.name')
}
const keysJson = localStorage.getItem('cashu.keys')
if (!keysJson) {
this.fetchMintKeys()
} else {
this.keys = JSON.parse(keysJson)
}
this.buyOrders = JSON.parse(
localStorage.getItem('cashu.buyOrders') || '[]'
)
@ -945,4 +988,5 @@ page_container %}
<script src="{{ url_for('cashu_static', path='js/noble-secp256k1.js') }}"></script>
<script src="{{ url_for('cashu_static', path='js/utils.js') }}"></script>
<script src="{{ url_for('cashu_static', path='js/dhke.js') }}"></script>
<script src="{{ url_for('cashu_static', path='js/base64.js') }}"></script>
{% endblock %}