feat: secret stuff
This commit is contained in:
parent
8af4f95cc1
commit
422711649f
|
@ -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.
|
||||
If it does not, it tries computing again a new x coordinate from the hash of the coordinate."""
|
||||
point = None
|
||||
msg = secret_msg
|
||||
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)
|
||||
except:
|
||||
msg = _hash
|
||||
|
||||
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_to_hash = message
|
||||
while point is None:
|
||||
try:
|
||||
_hash = hashlib.sha256(msg_to_hash).digest()
|
||||
point = PublicKey(b"\x02" + _hash, raw=True)
|
||||
except:
|
||||
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)
|
||||
|
||||
|
||||
|
|
|
@ -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}")
|
||||
|
||||
|
|
40
lnbits/extensions/cashu/static/js/base64.js
Normal file
40
lnbits/extensions/cashu/static/js/base64.js
Normal 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;
|
||||
|
||||
}({}));
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,12 +965,19 @@ 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') || '[]'
|
||||
)
|
||||
this.tokens = JSON.parse(localStorage.getItem('cashu.tokens') || '[]')
|
||||
console.log('### buyOrders',this.buyOrders)
|
||||
console.table('### tokens',this.tokens)
|
||||
console.log('### buyOrders', this.buyOrders)
|
||||
console.table('### tokens', this.tokens)
|
||||
console.log('#### this.mintId', this.mintId)
|
||||
console.log('#### this.mintName', this.mintName)
|
||||
|
||||
|
@ -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 %}
|
||||
|
|
Loading…
Reference in New Issue
Block a user