2023-02-12 00:03:29 -05:00
package nip26
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"strings"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/nbd-wtf/go-nostr"
)
type DelegationToken struct {
delegator [ 32 ] byte
token [ 64 ] byte
kinds [ ] int
since * time . Time
until * time . Time
tag [ 4 ] string
}
// Tag() returns the nostr formatted delegation tag for the DelegationToken d.
func ( d * DelegationToken ) Tag ( ) nostr . Tag {
return nostr . Tag ( d . tag [ : ] )
}
// Conditions() is the delegation conditions string as in NIP-26.
func ( d * DelegationToken ) Conditions ( ) ( conditions string ) {
for _ , k := range d . kinds {
conditions += fmt . Sprintf ( "kind=%d&" , k )
}
if d . since != nil {
conditions += fmt . Sprintf ( "created_at>%d&" , d . since . Unix ( ) )
}
if d . until != nil {
conditions += fmt . Sprintf ( "created_at<%d&" , d . until . Unix ( ) )
}
return strings . TrimSuffix ( conditions , "&" )
}
// This error is returned by d.Parse(ev) if the event ev does not have a delegation token.
var NoDelegationTag error = fmt . Errorf ( "No Delegation Tag" )
2023-02-12 00:39:29 -05:00
// This error is returned by Import(t,delegatee_pk) if the token signature verification fails.
var VerificationFailed error = fmt . Errorf ( "VerificationFailed" )
2023-02-12 00:03:29 -05:00
// CheckDelegation reads the event and reports whether or not it is correctly delegated.
// If there is a delegation tag, the delegation token signature will be checked according to NIP-26.
// If there is no delegation tag, ok will be true and err will be nil.
// For checking many events, it is advisable to use Parse to reduce the number of memory allocations.
func CheckDelegation ( ev * nostr . Event ) ( ok bool , err error ) {
d := new ( DelegationToken )
if ok , err := d . Parse ( ev ) ; ok == true && ( err == nil || err == NoDelegationTag ) {
return true , nil
} else {
return false , err
}
}
// Import verifies that t is NIP-26 delegation token for the given delegatee.
2023-02-12 00:39:29 -05:00
// The returned DelegationToken object can be used in DelegatedSign.
// If the token signature verification fails, the error VerificationFailed will be returned.
2023-02-12 00:03:29 -05:00
func Import ( t nostr . Tag , delegatee_pk string ) ( d * DelegationToken , e error ) {
d = new ( DelegationToken )
if len ( t ) == 4 && t [ 0 ] == "delegation" {
copy ( d . tag [ : ] , t )
} else {
return nil , fmt . Errorf ( "not a delegation tag" )
}
if n , e := hex . Decode ( d . delegator [ : ] , [ ] byte ( d . tag [ 1 ] ) ) ; n != 32 || e != nil {
return nil , fmt . Errorf ( "invalid delegation tag" )
}
if n , e := hex . Decode ( d . token [ : ] , [ ] byte ( d . tag [ 3 ] ) ) ; n != 64 || e != nil {
return nil , fmt . Errorf ( "invalid delegation tag" )
}
if d . kinds , d . since , d . until , e = parseConditions ( d . tag [ 2 ] ) ; e != nil {
return nil , fmt . Errorf ( "invalid conditions string" )
}
// compute the digest
h := sha256 . Sum256 ( [ ] byte ( fmt . Sprintf ( "nostr:delegation:%s:%s" , delegatee_pk , d . tag [ 2 ] ) ) )
sig , err := schnorr . ParseSignature ( d . token [ : ] )
if err != nil {
return nil , fmt . Errorf ( "Error: %s" , err . Error ( ) )
}
pubkey , err := schnorr . ParsePubKey ( d . delegator [ : ] )
if err != nil {
return nil , fmt . Errorf ( "Error: %s" , err . Error ( ) )
}
if ! sig . Verify ( h [ : ] , pubkey ) {
2023-02-12 00:39:29 -05:00
return nil , VerificationFailed
2023-02-12 00:03:29 -05:00
}
return d , nil
}
// Parse reads the event ev and stores the delegation token into d.
// The ok value verifies the event is correctly delegated.
// If there is no delegation token, then d will not be changed. In this case the error value will be `NoDelegationTag`, and ok will be set to true.
// Parse does NOT verify the event was correctly signed. Use ev.CheckSignature() for this.
// More efficient memory allocations versus CheckDelegation(ev) if many events need to be checked.
func ( d * DelegationToken ) Parse ( ev * nostr . Event ) ( ok bool , err error ) {
for _ , t := range ev . Tags {
if t [ 0 ] == "delegation" && len ( t ) == 4 {
copy ( d . tag [ : ] , t )
goto jump1
}
}
// event has no delegation. set the token to nil and return.
return true , NoDelegationTag
jump1 :
if n , e := hex . Decode ( d . delegator [ : ] , [ ] byte ( d . tag [ 1 ] ) ) ; n != 32 || e != nil {
return false , fmt . Errorf ( "invalid delegation tag" )
}
if n , e := hex . Decode ( d . token [ : ] , [ ] byte ( d . tag [ 3 ] ) ) ; n != 64 || e != nil {
return false , fmt . Errorf ( "invalid delegation tag" )
}
if d . kinds , d . since , d . until , err = parseConditions ( d . tag [ 2 ] ) ; err != nil {
return false , fmt . Errorf ( "invalid conditions string" )
}
if len ( d . kinds ) > 0 {
for _ , k := range d . kinds {
if ev . Kind == k {
goto jump2
}
}
return false , fmt . Errorf ( "Event kind %d is not allowed in delegation conditions." , ev . Kind )
}
jump2 :
if d . since != nil && ev . CreatedAt . Before ( * d . since ) {
return false , fmt . Errorf ( "Event is created before delegation conditions allow." )
}
if d . until != nil && ev . CreatedAt . After ( * d . until ) {
return false , fmt . Errorf ( "Event is created after delegation conditions allow." )
}
// compute the digest
h := sha256 . Sum256 ( [ ] byte ( fmt . Sprintf ( "nostr:delegation:%s:%s" , ev . PubKey , d . tag [ 2 ] ) ) )
sig , err := schnorr . ParseSignature ( d . token [ : ] )
if err != nil {
return false , fmt . Errorf ( "Error: %s" , err . Error ( ) )
}
pubkey , err := schnorr . ParsePubKey ( d . delegator [ : ] )
if err != nil {
return false , fmt . Errorf ( "Error: %s" , err . Error ( ) )
}
return sig . Verify ( h [ : ] , pubkey ) , nil
}
// DelegatedSign performs a delegated signature on the event ev.
// The delegation signature is not verified. If desired, the caller can ensure the delegation signature is correct by calling d.Parse(ev) or CheckDelegation(ev) afterwards.
func DelegatedSign ( ev * nostr . Event , d * DelegationToken , delegatee_sk string ) error {
for _ , t := range ev . Tags {
if t [ 0 ] == "delegation" {
return fmt . Errorf ( "event already has delegation token" )
}
}
if d . since != nil && ev . CreatedAt . Before ( * d . since ) || d . until != nil && ev . CreatedAt . After ( * d . until ) {
return fmt . Errorf ( "event created_at field is not compatible with delegation token time conditions." )
}
if len ( d . kinds ) > 0 {
for _ , k := range d . kinds {
if ev . Kind == k {
goto jump
}
}
return fmt . Errorf ( "event kind %d is not compatible with delegation token conditions." , ev . Kind )
}
jump :
if pk , e := nostr . GetPublicKey ( delegatee_sk ) ; e != nil {
return fmt . Errorf ( "invalid delegatee secret key." )
} else {
ev . PubKey = pk
}
ev . Tags = append ( ev . Tags , d . Tag ( ) )
return ev . Sign ( delegatee_sk )
}
// CreateToken creates a DelegationToken according to NIP-26.
func CreateToken ( delegator_sk string , delegatee_pk string , kinds [ ] int , since * time . Time , until * time . Time ) ( d * DelegationToken , e error ) {
d = new ( DelegationToken )
s , e := hex . DecodeString ( delegator_sk )
if e != nil {
return nil , fmt . Errorf ( "invalid delegator secret key" )
}
tee_pk , e := hex . DecodeString ( delegatee_pk )
if len ( tee_pk ) != 32 || e != nil {
return nil , fmt . Errorf ( "invalid delegatee pubkey" )
}
// set delegator
sk , tor_pk := btcec . PrivKeyFromBytes ( s )
copy ( d . delegator [ : ] , schnorr . SerializePubKey ( tor_pk ) )
2023-02-12 00:39:29 -05:00
d . kinds = kinds
2023-02-12 00:03:29 -05:00
d . since = since
d . until = until
// generate tag
d . tag [ 0 ] = "delegation"
d . tag [ 1 ] = fmt . Sprintf ( "%x" , d . delegator )
d . tag [ 2 ] = d . Conditions ( )
h := sha256 . Sum256 ( [ ] byte ( fmt . Sprintf ( "nostr:delegation:%x:%s" , tee_pk , d . tag [ 2 ] ) ) )
if sig , err := schnorr . Sign ( sk , h [ : ] ) ; err != nil {
panic ( err )
} else {
copy ( d . token [ : ] , sig . Serialize ( ) )
}
d . tag [ 3 ] = fmt . Sprintf ( "%x" , d . token )
return d , nil
}
func parseConditions ( conditions string ) ( kinds [ ] int , since * time . Time , until * time . Time , err error ) {
kinds = make ( [ ] int , 0 )
for _ , v := range strings . Split ( conditions , "&" ) {
switch {
case strings . HasPrefix ( v , "kind=" ) :
if i , e := strconv . ParseInt ( strings . TrimPrefix ( v , "kind=" ) , 10 , 64 ) ; e == nil {
kinds = append ( kinds , int ( i ) )
} else {
return nil , nil , nil , fmt . Errorf ( "Invalid: %s!" , v )
}
case strings . HasPrefix ( v , "created_at>" ) && since == nil :
if i , e := strconv . ParseInt ( strings . TrimPrefix ( v , "created_at>" ) , 10 , 64 ) ; e == nil {
t := time . Unix ( i , 0 )
since = & t
} else {
return nil , nil , nil , fmt . Errorf ( "Invalid: %s" , v )
}
case strings . HasPrefix ( v , "created_at<" ) && until == nil :
if i , e := strconv . ParseInt ( strings . TrimPrefix ( v , "created_at<" ) , 10 , 64 ) ; e == nil {
t := time . Unix ( i , 0 )
until = & t
} else {
return nil , nil , nil , fmt . Errorf ( "Invalid: %s" , v )
}
default :
return nil , nil , nil , fmt . Errorf ( "Invalid: %s" , v )
}
}
return
}