1+ /********************************************************************************************/
2+ /*
3+ /* ___ _ _ ___ _ _ _ _
4+ /* / __|_ __ ___ ___| |_| |_ / __|_ _ _ _ _ __| |_ ___ | | (_) |__
5+ /* \__ \ ' \/ _ \/ _ \ _| ' \ | (__| '_| || | '_ \ _/ _ \ | |__| | '_ \
6+ /* |___/_|_|_\___/\___/\__|_||_| \___|_| \_, | .__/\__\___/ |____|_|_.__/
7+ /* |__/|_|
8+ /*
9+ /* Copyright (C) 2024 - Renaud Dubois - This file is part of SCL (Smooth CryptoLib) project
10+ /* License: This software is licensed under MIT License
11+ /********************************************************************************************/
12+ import { nonce_gen_internal , nonce_agg , key_agg , IndividualPubKey , psign , partial_sig_verify_internal } from './bip327.mjs'
13+
14+ import { utils , getPublicKey } from '@noble/secp256k1' ;
15+
16+
17+ import { SCL_ecc } from './SCL_ecc.mjs' ;
18+ import { SCL_Musig2 } from './SCL_Musig2.mjs' ;
19+
20+ import { randomBytes } from 'crypto' ; // Use Node.js's crypto module
21+
22+
23+
24+ //the Alice adaptator signature
25+ //sk_A is Alice secret Key
26+ //rA is the secnonce
27+ //R is the public agg nonce
28+ //Pub_AB is the Musig2 agreed multisig key between Alice and Bob
29+ //Pub_A is Alice public key
30+
31+ export function psign_adapt ( psig , t ) {
32+
33+ sprime = ( int_from_bytes ( psig ) + int_from_bytes ( t ) ) % secp256k1 . CURVE . n ;
34+
35+ return sprime ;
36+ }
37+
38+ //check that a tweaked partially signature is valid
39+ export function partial_adaptatorsig_verify_internal ( psig , pubnonce , pk , session_ctx , T ) {
40+
41+ return true ;
42+ }
43+
44+ export function atomic_check ( tG , psA1 , psA2 , psB1 , psB2 , QA , R , msg1 , msg2 ) {
45+
46+ return true ;
47+ }
48+
49+
50+ export function get_tweak_from_sigs ( sAp , sB , sAB )
51+ {
52+ const sABp = partial_sig_agg ( [ sAp , sB ] ) ;
53+ t = ( sABp - sAB ) % secp256k1 . CURVE . n ;
54+ return t ;
55+ }
56+
57+ //the function takes as input an adaptator signature, its tweak t, and a valid signature, and returns the Musig2 corresponding signature
58+ export function sign_untweak ( t , psigA_adapt , psigB ) {
59+ const sABp = partial_sig_agg ( [ psigA_adapt , psigB ] ) ;
60+
61+ const sAB = ( sABp - t ) % secp256k1 . CURVE . n ;
62+
63+ return sAB ;
64+ }
65+
66+ export function atomic_example ( ) {
67+
68+ // Alice and Bob private keys
69+ const skA = utils . randomPrivateKey ( ) ;
70+ console . log ( "sk" , skA ) ;
71+ const skB = utils . randomPrivateKey ( ) ;
72+
73+ //Alice and Bob public keys
74+ const QA = IndividualPubKey ( skA ) ;
75+ const QB = IndividualPubKey ( skB ) ;
76+
77+ const pubkeys = [ QA , QB ] ;
78+
79+ const msg1 = Buffer . from ( "Unlock 1strkBTC on Starknet to Alice" , 'utf-8' ) ;
80+ console . log ( "msg1=" , msg1 ) ;
81+ const msg2 = Buffer . from ( "Unlock 1WBTC on Ethereum to Bob" , 'utf-8' ) ;
82+
83+
84+ //key aggregation
85+ const QAB = key_agg ( [ QA , QB ] ) [ 0 ] ;
86+ console . log ( "QAB=" , QAB ) ;
87+
88+ //nonce generation
89+
90+ const nonceA1 = nonce_gen_internal ( utils . randomPrivateKey ( ) , skA , QA , QAB , msg1 , Buffer . from ( "" ) ) ; //alice generates its nonce
91+ const nonceA2 = nonce_gen_internal ( utils . randomPrivateKey ( ) , skA , QA , QAB , msg2 , Buffer . from ( "" ) ) ; //alice generates its nonce
92+
93+ const nonceB1 = nonce_gen_internal ( utils . randomPrivateKey ( ) , skB , QB , QAB , msg1 , Buffer . from ( "" ) ) ; //alice generates its nonce
94+ const nonceB2 = nonce_gen_internal ( utils . randomPrivateKey ( ) , skB , QB , QAB , msg2 , Buffer . from ( "" ) ) ; //alice generates its nonce
95+
96+
97+ //alice and Bob construct common nonce from pubnonces
98+ const R1 = nonce_agg ( [ nonceA1 [ 1 ] , nonceB1 [ 1 ] ] ) ;
99+ const R2 = nonce_agg ( [ nonceA2 [ 1 ] , nonceB2 [ 1 ] ] ) ;
100+
101+ session_ctx1 = [ R1 , pubkeys , [ ] , [ ] , msg1 ] ;
102+ session_ctx2 = [ R1 , pubkeys , [ ] , [ ] , msg2 ] ;
103+
104+
105+ //Alice locks one BTC on Ethereum, using the corresponding Musig2 adress, it is unlocked with msg2 multisig or timelock expiration
106+ //bob locks one BTC on starknet, using the corresponding Musig2 adress, it is unlocked with msg1 multisig or timelock expiration
107+
108+ //alice generates a secret adaptator tweak and publish offchain the value tG
109+ const t = utils . randomPrivateKey ( ) ;
110+
111+ //alice generates the secret adaptator signatures sA'1 and sA'2 for both message and broadcast them offchain
112+ const psigA1 = psign ( nonceA1 [ 0 ] , skA , session_ctx1 )
113+ const sAp1 = psign_adapt ( psigA1 , t ) ;
114+ const psigA2 = psign ( nonceA2 [ 0 ] , skA , session_ctx1 )
115+ const sAp2 = psign_adapt ( psigA2 , t ) ;
116+
117+ //bob check the compliance, then broadcast offchain signature of message 1 sb1
118+ psigB1 = psign ( nonceB1 , skB , session_ctx1 ) ;
119+
120+ //Alice unlocks its strkBTC, using sb1, thus revealing the tweak to Bob
121+ sAB1 = partial_sig_agg ( [ psigA1 , psigB1 ] , session_ctx1 ) ;
122+
123+ //Bob reads onchain 1 the value of t, then compute the value of SAB2, unlocking its token
124+ const rec_t = get_tweak_from_sigs ( sAB1 , sAp1 , psigB1 ) ;
125+ const sAB2 = sign_untweak ( psigA2 ) ;
126+
127+ }
128+
129+ export class SCL_Atomic_Swap
130+ {
131+ constructor ( curve ) {
132+ this . signer = new SCL_Musig2 ( curve ) ;
133+
134+ this . curve = signer . curve ;
135+ }
136+
137+ Psign_adapt ( psig , t ) {
138+
139+
140+ let sprime = ( int_from_bytes ( psig ) + t ) % this . curve . order ;
141+ let G = this . curve . GetBase ( t ) ;
142+
143+ return [ sprime , G . multiply ( t ) ] ;
144+ }
145+
146+ Untweak ( t , psigA_adapt , psigB ) {
147+ const sABp = partial_sig_agg ( [ psigA_adapt , psigB ] ) ;
148+
149+ const sAB = ( sABp - t ) % secp256k1 . CURVE . n ;
150+
151+ return sAB ;
152+ }
153+
154+ }
155+
156+
157+ function test_full_atomic_session ( curve ) {
158+ const swapper = new SCL_Atomic_Swap ( curve ) ;
159+ const signer = swapper . signer ;
160+
161+ console . log ( "/*************************** " ) ;
162+ console . log ( "Test full Atomic session on curve" , Curve ) ;
163+
164+ console . log ( " -Generate random keys" ) ;
165+
166+ const sk1 = signer . curve . Get_Random_privateKey ( ) ; //this provides a 32 bytes array
167+ const sk2 = signer . curve . Get_Random_privateKey ( ) ;
168+
169+ console . log ( "sk1=" , sk1 ) ;
170+ console . log ( "sk2=" , sk2 ) ;
171+ let seckeys = [ sk1 , sk2 ] ;
172+
173+ const pubK1 = signer . IndividualPubKey_array ( sk1 ) ;
174+ const pubK2 = signer . IndividualPubKey_array ( sk2 ) ;
175+
176+ console . log ( "pubK1=" , pubK1 ) ;
177+ console . log ( "pubK2=" , pubK2 ) ;
178+
179+ const pubkeys = [ pubK1 , pubK2 ] ;
180+
181+ let aggpk = signer . Key_agg ( pubkeys ) [ 0 ] ; //here aggpk is a 32 or 33 bytes compressed public key
182+ let x_aggpk = signer . curve . ForceXonly ( aggpk ) ; //x-only version for noncegen, allways 32
183+
184+ console . log ( "Aggregated Pubkey:" , aggpk ) ;
185+
186+ const msg1 = Buffer . from ( "Unlock 1strkBTC on Starknet to Alice" , 'utf-8' ) ;
187+ console . log ( "msg1=" , msg1 ) ;
188+ const msg2 = Buffer . from ( "Unlock 1WBTC on Ethereum to Bob" , 'utf-8' ) ;
189+ console . log ( "msg2=" , msg2 ) ;
190+
191+
192+ //nonce generation
193+ console . log ( " -Generate random Nonces with commitment" , aggpk ) ;
194+ //diversification chain
195+ const extra_in1 = Buffer . from ( randomBytes ( 32 ) ) ;
196+ const extra_in2 = Buffer . from ( randomBytes ( 32 ) ) ;
197+
198+
199+ let nonceA1 = signer . Nonce_gen ( seckeys [ 0 ] , pubkeys [ 0 ] , x_aggpk , msg1 , extra_in1 ) ;
200+ let nonceB1 = signer . Nonce_gen ( seckeys [ 1 ] , pubkeys [ 1 ] , x_aggpk , msg2 , extra_in1 ) ;
201+
202+ let nonceA2 = signer . Nonce_gen ( seckeys [ 0 ] , pubkeys [ 0 ] , x_aggpk , msg1 , extra_in2 ) ;
203+ let nonceB2 = signer . Nonce_gen ( seckeys [ 1 ] , pubkeys [ 1 ] , x_aggpk , msg2 , extra_in2 ) ;
204+
205+
206+ //aggregation of public nonces
207+ let aggnonce1 = signer . Nonce_agg ( [ nonceA1 [ 1 ] . toString ( 'hex' ) , nonceB1 [ 1 ] . toString ( 'hex' ) ] ) ;
208+ let aggnonce2 = signer . Nonce_agg ( [ nonceA2 [ 1 ] . toString ( 'hex' ) , nonceB2 [ 1 ] . toString ( 'hex' ) ] ) ;
209+
210+
211+ const session_ctx1 = [ aggnonce1 , pubkeys , [ ] , [ ] , msg ] ;
212+ const session_ctx2 = [ aggnonce2 , pubkeys , [ ] , [ ] , msg ] ;
213+
214+ let pA1 = signer . Psign ( nonceA1 [ 0 ] , seckeys [ 0 ] , session_ctx1 ) ;
215+ let pA2 = signer . Psign ( nonceA2 [ 0 ] , seckeys [ 0 ] , session_ctx2 ) ;
216+
217+
218+ let pB1 = signer . Psign ( nonceB1 [ 0 ] , seckeys [ 0 ] , session_ctx1 ) ;
219+ let pB2 = signer . Psign ( nonceB2 [ 0 ] , seckeys [ 0 ] , session_ctx2 ) ;
220+
221+ //Alice tweaks signatures
222+ let t = int_from_bytes ( signer . curve . Get_Random_privateKey ( ) ) ;
223+ let atomic_ctx1 = swapper . Psign_adapt ( pA1 , t ) ;
224+ let atomic_ctx2 = swapper . Psign_adapt ( pA2 , t ) ;
225+
226+ //todo:Bob checks compliance of tG, pA2p, pA1p, msg1, msg2
227+
228+
229+ //Bob compute sAB', then substract to obtain sAB
230+ let sAB1 = swapper . Untweak ( t , atomic_ctx1 ) ;
231+
232+ //
233+
234+
235+
236+
237+ console . log ( "p1=" , p1 ) ;
238+
239+ }
0 commit comments