Skip to content

Commit

Permalink
Working on Sign and Validate etc
Browse files Browse the repository at this point in the history
  • Loading branch information
mrz1836 committed Oct 28, 2020
1 parent 3b492ea commit 4b31c90
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 70 deletions.
171 changes: 102 additions & 69 deletions aip.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package aip

import (
"bytes"
"encoding/hex"
"fmt"
"net"
Expand All @@ -17,7 +16,6 @@ import (
"github.com/bitcoinschema/go-bitcoin"
"github.com/bitcoinschema/go-bob"
"github.com/bitcoinsv/bsvutil"
"github.com/libsv/libsv/transaction/output"
"github.com/tonicpow/go-paymail"
)

Expand All @@ -36,11 +34,12 @@ const (

// Aip is an Author Identity Protocol object
type Aip struct {
Algorithm Algorithm
Address string
Data []string
Signature string
Indices []int `json:"indices,omitempty" bson:"indices,omitempty"`
Algorithm Algorithm // Different algorithms
Address string // Bitcoin address
Paymail string // handle@domain.com
Data []string // Data to sign
Signature string // Signature of the given data
Indices []int `json:"indices,omitempty" bson:"indices,omitempty"` // Indices for BOB
}

// FromTape takes a BOB Tape and returns a Aip data structure.
Expand Down Expand Up @@ -116,32 +115,44 @@ func (a *Aip) Validate() bool {
if len(a.Data) == 0 {
return false
}
switch a.Algorithm {
case BitcoinECDSA:
// Validate verifies a Bitcoin signed message signature
return bitcoin.VerifyMessage(a.Address, a.Signature, strings.Join(a.Data, "")) == nil
case Paymail:
if len(a.Address) == 0 {
return false
}

// Get the PKI for the address
pki, err := getPki(a.Address)
if err != nil {
// Different way to get the address from Paymail handle
if a.Algorithm == Paymail {
if len(a.Paymail) == 0 {
return false
}

// Get the public address for this paymail from pki
var addr *bsvutil.LegacyAddressPubKeyHash
if addr, err = bitcoin.GetAddressFromPubKeyString(pki.PubKey); err != nil {
return false
// Get the PKI for the address (stores an address from a PubKey)
if len(a.Address) == 0 {
if err := a.GetAddressForPaymail(); err != nil {
return false
}
}
}

// You get the address associated with the pki instead of the current address
return bitcoin.VerifyMessage(addr.String(), a.Signature, strings.Join(a.Data, "")) == nil
default:
return false
// Verify the address + signature + data
return bitcoin.VerifyMessage(a.Address, a.Signature, strings.Join(a.Data, "")) == nil
}

// GetAddressForPaymail will get the address of the PubKey associated with a Paymail address
// This will do a remote lookup
func (a *Aip) GetAddressForPaymail() error {
if len(a.Paymail) == 0 {
return fmt.Errorf("missing paymail address")
}

pki, err := getPki(a.Paymail)
if err != nil {
return err
}

// Get the public address for this paymail from pki
var addr *bsvutil.LegacyAddressPubKeyHash
if addr, err = bitcoin.GetAddressFromPubKeyString(pki.PubKey); err != nil {
return err
}
a.Address = addr.String()
return nil
}

// NewFromTape will create a new AIP object from a bob.Tape
Expand All @@ -154,8 +165,9 @@ func NewFromTape(tape bob.Tape) (a *Aip) {
// SignBobOpReturnData appends a signature to a BOB Tx by adding a
// protocol separator push_data followed by AIP information
func SignBobOpReturnData(privateKey string, algorithm Algorithm,
addressString string, output bob.Output) (*bob.Output, *Aip, error) {
output bob.Output, paymailAddress, paymailPubKey string) (*bob.Output, *Aip, error) {

// get data to sign from bob tx
var dataToSign []string
for _, tape := range output.Tape {
for _, cell := range tape.Cell {
Expand All @@ -169,18 +181,8 @@ func SignBobOpReturnData(privateKey string, algorithm Algorithm,
}
}

// get data to sign from bob tx
if algorithm == Paymail {
// pubkey is used, derive address
addr, err := bitcoin.GetAddressFromPubKeyString(addressString)
if err != nil {
return nil, nil, err
}
addressString = addr.String()
}

// Sign the data
a, err := Sign(privateKey, algorithm, strings.Join(dataToSign, ""), addressString)
a, err := Sign(privateKey, algorithm, strings.Join(dataToSign, ""), paymailAddress, paymailPubKey)
if err != nil {
return nil, nil, err
}
Expand All @@ -194,8 +196,8 @@ func SignBobOpReturnData(privateKey string, algorithm Algorithm,
H: hex.EncodeToString([]byte(algorithm)),
S: string(algorithm),
}, {
H: hex.EncodeToString([]byte(addressString)),
S: addressString,
H: hex.EncodeToString([]byte(paymailAddress)),
S: paymailAddress,
}, {
H: hex.EncodeToString([]byte(a.Signature)),
S: a.Signature,
Expand All @@ -205,7 +207,7 @@ func SignBobOpReturnData(privateKey string, algorithm Algorithm,
return &output, a, nil
}

// SignOpReturnData will append the given data and return an output.Output
/*// SignOpReturnData will append the given data and return an output.Output
func SignOpReturnData(privateKey string, algorithm Algorithm,
addressString string, data [][]byte) (*output.Output, *Aip, error) {
Expand All @@ -217,6 +219,8 @@ func SignOpReturnData(privateKey string, algorithm Algorithm,
return nil, nil, err
}
addressString = addr.String()
} else {
addressString = "" // todo: hack right now for the Sign() taking two types of signing methods
}
// Sign with AIP
Expand All @@ -242,38 +246,67 @@ func SignOpReturnData(privateKey string, algorithm Algorithm,
return out, a, nil
}
*/

// Sign will provide an AIP signature for a given private key and message
// Just set paymail = "" when using BitcoinECDSA signature
func Sign(privateKey string, algorithm Algorithm, message, paymailAddress string) (a *Aip, err error) {

// Create the base AIP object
a = &Aip{Algorithm: algorithm, Data: []string{message}}
var sig string

// If you don't know the paymailPubKey leave it blank as it will be fetched from the Paymail provider
func Sign(privateKey string, algorithm Algorithm, message, paymailAddress, paymailPubKey string) (*Aip, error) {
// Sign using different algorithms
switch algorithm {
case BitcoinECDSA:
if paymailAddress != "" {
err = fmt.Errorf("paymail is provided but algorithm is: %s", BitcoinECDSA)
return
}
if sig, err = bitcoin.SignMessage(privateKey, message); err != nil {
return
}
a.Signature = sig
var address string
if address, err = bitcoin.GetAddressFromPrivateKey(privateKey); err != nil {
return
}
a.Address = address
return SignUsingECDSA(privateKey, message)
case Paymail:
if sig, err = bitcoin.SignMessage(privateKey, message); err != nil {
return
}
a.Signature = sig
_, _, a.Address = paymail.SanitizePaymail(paymailAddress)
return SignUsingPaymailPubKey(privateKey, message, paymailAddress, paymailPubKey)
default:
return nil, fmt.Errorf("algorithm: %v unknown", algorithm)
}
}

// SignUsingECDSA will use the BitcoinECDSA format for signing
func SignUsingECDSA(privateKey, message string) (a *Aip, err error) {
a = &Aip{Algorithm: BitcoinECDSA, Data: []string{message}}
if a.Address, err = bitcoin.GetAddressFromPrivateKey(privateKey); err != nil {
return
}
a.Signature, err = bitcoin.SignMessage(privateKey, message)
return
}

// SignUsingPaymail will use the Paymail format for signing
func SignUsingPaymail(privateKey, message, paymailAddress string) (a *Aip, err error) {
a = &Aip{Algorithm: Paymail, Data: []string{message}}

// Set the paymail address
_, _, a.Paymail = paymail.SanitizePaymail(paymailAddress)

// Get the address via remote lookup
if err = a.GetAddressForPaymail(); err != nil {
return
}

// Sign the message
a.Signature, err = bitcoin.SignMessage(privateKey, message)
return
}

// SignUsingPaymailPubKey will sign using a given pubKey (Paymail)
// This method is preferred to SignUsingPaymail() if you already have the pubKey present
func SignUsingPaymailPubKey(privateKey, message, paymailAddress, pubKey string) (a *Aip, err error) {
a = &Aip{Algorithm: Paymail, Data: []string{message}}

// Set the paymail address
_, _, a.Paymail = paymail.SanitizePaymail(paymailAddress)

// Get the address from given paymailPubKey
var addr *bsvutil.LegacyAddressPubKeyHash
if addr, err = bitcoin.GetAddressFromPubKeyString(pubKey); err != nil {
return
}
a.Address = addr.String()

// Sign the message
a.Signature, err = bitcoin.SignMessage(privateKey, message)
return
}

Expand All @@ -291,7 +324,7 @@ func ValidateTapes(tapes []bob.Tape) bool {
}

// getPki will fetch the PKI info for a given paymail
func getPki(paymailString string) (*paymail.PKI, error) {
func getPki(paymailAddress string) (*paymail.PKI, error) {

// Load the client
client, err := paymail.NewClient(nil, nil)
Expand All @@ -300,16 +333,16 @@ func getPki(paymailString string) (*paymail.PKI, error) {
}

// Parse the paymail address
alias, domain, _ := paymail.SanitizePaymail(paymailString)
alias, domain, _ := paymail.SanitizePaymail(paymailAddress)

// Find the srv record
var srv *net.SRV
srv, err = client.GetSRVRecord(paymail.DefaultServiceName, paymail.DefaultProtocol, domain)
if err != nil {
return nil, err
}

// Get the capabilities
// This is required first to get the corresponding PKI endpoint url
// Get the capabilities to get the corresponding PKI endpoint url
var capabilities *paymail.Capabilities
if capabilities, err = client.GetCapabilities(srv.Target, paymail.DefaultPort); err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion aip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func TestSignUsingPaymail(t *testing.T) {
}

var signedOutput *bob.Output
signedOutput, _, err = SignBobOpReturnData(pk, Paymail, hex.EncodeToString(publicKey.SerializeCompressed()), bobTx.Out[0])
signedOutput, _, err = SignBobOpReturnData(pk, Paymail, bobTx.Out[0], "some@domain.com", hex.EncodeToString(publicKey.SerializeCompressed()))
if err != nil {
t.Fatalf("error occurred: %s", err.Error())
}
Expand Down

0 comments on commit 4b31c90

Please sign in to comment.