validate SECP256k1 signatures
This commit is contained in:
parent
c62c175d8b
commit
9a629bb545
9 changed files with 205 additions and 89 deletions
1
build.sh
1
build.sh
|
@ -4,3 +4,4 @@ set -euxo pipefail
|
|||
echo "Building protobuf files"
|
||||
rm -rf pb/*.pb.go
|
||||
protoc --go_out=. pb/*.proto
|
||||
go build ./...
|
|
@ -7,27 +7,29 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
type Claim struct {
|
||||
protobuf pb.Claim
|
||||
type ClaimHelper struct {
|
||||
*pb.Claim
|
||||
}
|
||||
|
||||
func (claim *Claim) LoadFromBytes(raw_claim []byte) (error) {
|
||||
if claim.protobuf.String() != "" {
|
||||
func (claim *ClaimHelper) LoadFromBytes(raw_claim []byte) (error) {
|
||||
if claim.String() != "" {
|
||||
return errors.New("already initialized")
|
||||
}
|
||||
if len(raw_claim) < 1 {
|
||||
return errors.New("there is nothing to decode")
|
||||
}
|
||||
|
||||
claim_pb := &pb.Claim{}
|
||||
err := proto.Unmarshal(raw_claim, claim_pb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
claim.protobuf = *claim_pb
|
||||
*claim = ClaimHelper{claim_pb}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (claim *Claim) LoadFromHexString(claim_hex string) (error) {
|
||||
func (claim *ClaimHelper) LoadFromHexString(claim_hex string) (error) {
|
||||
buf, err := hex.DecodeString(claim_hex)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -35,8 +37,8 @@ func (claim *Claim) LoadFromHexString(claim_hex string) (error) {
|
|||
return claim.LoadFromBytes(buf)
|
||||
}
|
||||
|
||||
func DecodeClaimBytes(serialized []byte) (*Claim, error) {
|
||||
claim := &Claim{}
|
||||
func DecodeClaimBytes(serialized []byte) (*ClaimHelper, error) {
|
||||
claim := &ClaimHelper{&pb.Claim{}}
|
||||
err := claim.LoadFromBytes(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -44,10 +46,31 @@ func DecodeClaimBytes(serialized []byte) (*Claim, error) {
|
|||
return claim, nil
|
||||
}
|
||||
|
||||
func DecodeClaimHex(serialized string) (*Claim, error) {
|
||||
func DecodeClaimHex(serialized string) (*ClaimHelper, error) {
|
||||
claim_bytes, err := hex.DecodeString(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DecodeClaimBytes(claim_bytes)
|
||||
}
|
||||
|
||||
func (m *ClaimHelper) GetStream() *pb.Stream {
|
||||
if m != nil {
|
||||
return m.Stream
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ClaimHelper) GetCertificate() *pb.Certificate {
|
||||
if m != nil {
|
||||
return m.Certificate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ClaimHelper) GetPublisherSignature() *pb.Signature {
|
||||
if m != nil {
|
||||
return m.PublisherSignature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -14,15 +14,10 @@ var raw_claims = []string{
|
|||
|
||||
func TestDecodeClaims(t *testing.T) {
|
||||
for _, claim_hex := range(raw_claims) {
|
||||
claim := Claim{}
|
||||
err := claim.LoadFromHexString(claim_hex)
|
||||
claim, err := DecodeClaimHex(claim_hex)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = claim.LoadFromHexString(claim_hex)
|
||||
if err.Error() != "already initialized" {
|
||||
t.Error(err)
|
||||
}
|
||||
serialized_hex, err := claim.SerializedHexString()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@ -35,8 +30,7 @@ func TestDecodeClaims(t *testing.T) {
|
|||
|
||||
func TestStripSignature(t *testing.T) {
|
||||
claim_hex := raw_claims[1]
|
||||
claim := Claim{}
|
||||
err := claim.LoadFromHexString(claim_hex)
|
||||
claim, err := DecodeClaimHex(claim_hex)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -45,6 +39,6 @@ func TestStripSignature(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
if hex.EncodeToString(no_sig) != raw_claims[2] {
|
||||
t.Error("Failed to remove signature")
|
||||
t.Error("failed to remove signature")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,13 @@ import (
|
|||
"github.com/golang/protobuf/jsonpb"
|
||||
)
|
||||
|
||||
func (claim *Claim) RenderJSON() (string, error) {
|
||||
marshaler := jsonpb.Marshaler{}
|
||||
return marshaler.MarshalToString(&claim.protobuf)
|
||||
func marshalToString(claim *ClaimHelper) (string, error) {
|
||||
m_pb := &jsonpb.Marshaler{}
|
||||
return m_pb.MarshalToString(claim)
|
||||
}
|
||||
|
||||
func (claim *ClaimHelper) RenderJSON() (string, error) {
|
||||
return marshalToString(claim)
|
||||
}
|
||||
|
||||
//TODO: encode byte arrays with b58 for addresses and b16 for source hashes instead of the default of b64
|
|
@ -1,21 +1,42 @@
|
|||
package claim
|
||||
|
||||
import (
|
||||
"../pb"
|
||||
"errors"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"encoding/hex"
|
||||
"../pb"
|
||||
)
|
||||
|
||||
func (claim *Claim) Serialized() ([]byte, error) {
|
||||
serialized := claim.protobuf.String()
|
||||
func (claim *ClaimHelper) Serialized() ([]byte, error) {
|
||||
serialized := claim.String()
|
||||
if serialized == "" {
|
||||
return nil, errors.New("not initialized")
|
||||
}
|
||||
return proto.Marshal(&claim.protobuf)
|
||||
v := claim.GetVersion()
|
||||
t := claim.GetClaimType()
|
||||
|
||||
return proto.Marshal(
|
||||
&pb.Claim{
|
||||
Version: &v,
|
||||
ClaimType: &t,
|
||||
Stream: claim.GetStream(),
|
||||
Certificate: claim.GetCertificate(),
|
||||
PublisherSignature: claim.GetPublisherSignature()})
|
||||
}
|
||||
|
||||
func (claim *Claim) SerializedHexString() (string, error) {
|
||||
func (claim *ClaimHelper) GetProtobuf() (*pb.Claim) {
|
||||
v := claim.GetVersion()
|
||||
t := claim.GetClaimType()
|
||||
|
||||
return &pb.Claim{
|
||||
Version: &v,
|
||||
ClaimType: &t,
|
||||
Stream: claim.GetStream(),
|
||||
Certificate: claim.GetCertificate(),
|
||||
PublisherSignature: claim.GetPublisherSignature()}
|
||||
}
|
||||
|
||||
func (claim *ClaimHelper) SerializedHexString() (string, error) {
|
||||
serialized, err := claim.Serialized()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -24,11 +45,11 @@ func (claim *Claim) SerializedHexString() (string, error) {
|
|||
return serialized_hex, nil
|
||||
}
|
||||
|
||||
func (claim *Claim) SerializedNoSignature() ([]byte, error) {
|
||||
if claim.protobuf.String() == "" {
|
||||
func (claim *ClaimHelper) SerializedNoSignature() ([]byte, error) {
|
||||
if claim.String() == "" {
|
||||
return nil, errors.New("not initialized")
|
||||
}
|
||||
if claim.protobuf.GetPublisherSignature() == nil {
|
||||
if claim.GetPublisherSignature() == nil {
|
||||
serialized, err := claim.Serialized()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -36,7 +57,7 @@ func (claim *Claim) SerializedNoSignature() ([]byte, error) {
|
|||
return serialized, nil
|
||||
} else {
|
||||
clone := &pb.Claim{}
|
||||
proto.Merge(clone, &claim.protobuf)
|
||||
proto.Merge(clone, claim.GetProtobuf())
|
||||
proto.ClearAllExtensions(clone.PublisherSignature)
|
||||
clone.PublisherSignature = nil
|
||||
return proto.Marshal(clone)
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package claim
|
||||
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"../pb"
|
||||
"encoding/hex"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
|
||||
func TestValidateClaimSignature(t *testing.T) {
|
||||
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
|
||||
signed_claim_hex := "080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
|
||||
|
||||
signed_claim := &pb.Claim{}
|
||||
cert_claim := &pb.Claim{}
|
||||
|
||||
buf, _ := hex.DecodeString(signed_claim_hex)
|
||||
proto.Unmarshal(buf, signed_claim)
|
||||
buf, _ = hex.DecodeString(cert_claim_hex)
|
||||
proto.Unmarshal(buf, cert_claim)
|
||||
|
||||
//signed_claim := DecodeAddress("bSkUov7HMWpYBiXackDwRnR5ishhGHvtJt")
|
||||
//cert_claim_id, _ := hex.DecodeString("251305ca93d4dbedb50dceb282ebcb7b07b7ac65")
|
||||
|
||||
}
|
||||
|
||||
// 251305ca93d4dbedb50dceb282ebcb7b07b7ac65
|
||||
|
||||
// bSkUov7HMWpYBiXackDwRnR5ishhGHvtJt
|
|
@ -4,39 +4,99 @@ import (
|
|||
"../address"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"encoding/asn1"
|
||||
"crypto/x509/pkix"
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"math/big"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func GetClaimSignatureDigest(claim_address [25]byte, certificate_id [20]byte, serialized_no_sig []byte) [32]byte {
|
||||
type publicKeyInfo struct {
|
||||
Raw asn1.RawContent
|
||||
Algorithm pkix.AlgorithmIdentifier
|
||||
PublicKey asn1.BitString
|
||||
}
|
||||
|
||||
const SECP256k1 = "SECP256k1"
|
||||
|
||||
func GetClaimSignatureDigest(claimAddress [25]byte, certificateId [20]byte, serializedNoSig []byte) [32]byte {
|
||||
combined := []byte{}
|
||||
for _, c := range claim_address {combined = append(combined, c)}
|
||||
for _, c := range serialized_no_sig {combined = append(combined, c)}
|
||||
for _, c := range certificate_id {combined = append(combined, c)}
|
||||
for _, c := range claimAddress {combined = append(combined, c)}
|
||||
for _, c := range serializedNoSig {combined = append(combined, c)}
|
||||
for _, c := range certificateId {combined = append(combined, c)}
|
||||
digest := sha256.Sum256(combined)
|
||||
return [32]byte(digest)
|
||||
}
|
||||
|
||||
func VerifyDigest(signature [64]byte, digest [32]byte) bool {
|
||||
/*
|
||||
def verify_digest(self, signature, digest, sigdecode=sigdecode_string):
|
||||
if len(digest) > self.curve.baselen:
|
||||
raise BadDigestError("this curve (%s) is too short "
|
||||
"for your digest (%d)" % (self.curve.name,
|
||||
8*len(digest)))
|
||||
number = string_to_number(digest)
|
||||
r, s = sigdecode(signature, self.pubkey.order)
|
||||
sig = ecdsa.Signature(r, s)
|
||||
if self.pubkey.verifies(number, sig):
|
||||
return True
|
||||
raise BadSignatureError
|
||||
*/
|
||||
return true
|
||||
func (claim *ClaimHelper) GetCertificatePublicKey() (btcec.PublicKey, error) {
|
||||
derBytes := claim.GetCertificate().GetPublicKey()
|
||||
pub := publicKeyInfo{}
|
||||
asn1.Unmarshal(derBytes, &pub)
|
||||
pubkey_bytes := []byte(pub.PublicKey.Bytes)
|
||||
p, err := btcec.ParsePubKey(pubkey_bytes, btcec.S256())
|
||||
if err != nil {
|
||||
fmt.Println("parse public key error: ", err)
|
||||
}
|
||||
return *p, err
|
||||
}
|
||||
|
||||
func (claim *Claim) ValidateClaimSignature(claim_address [25]byte, certificate_id [20]byte, signature [64]byte) (bool, error) {
|
||||
claim_address, err := address.ValidateAddress(claim_address)
|
||||
if err != nil {return false, errors.New("invalid address")}
|
||||
serialized_no_sig, err := claim.SerializedNoSignature()
|
||||
if err != nil {return false, errors.New("serialization error")}
|
||||
claim_digest := GetClaimSignatureDigest(claim_address, certificate_id, serialized_no_sig)
|
||||
return VerifyDigest(signature, claim_digest), nil
|
||||
func (claim *ClaimHelper) VerifyDigest(certificate *ClaimHelper, signature [64]byte, digest [32]byte) bool {
|
||||
public_key, err := certificate.GetCertificatePublicKey()
|
||||
if err != nil {
|
||||
fmt.Println("parse public key error: ", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if claim.PublisherSignature.SignatureType.String() == SECP256k1 {
|
||||
R := &big.Int{}
|
||||
S := &big.Int{}
|
||||
R.SetBytes(signature[0:32])
|
||||
S.SetBytes(signature[32:64])
|
||||
return ecdsa.Verify(public_key.ToECDSA(), digest[:], R, S)
|
||||
}
|
||||
fmt.Println("unknown curve:", claim.PublisherSignature.SignatureType.String())
|
||||
return false
|
||||
}
|
||||
|
||||
func (claim *ClaimHelper) ValidateClaimSignatureBytes(certificate *ClaimHelper, claimAddress [25]byte, certificateId [20]byte) (bool, error) {
|
||||
signature := claim.GetPublisherSignature()
|
||||
if signature == nil {
|
||||
return false, errors.New("claim does not have a signature")
|
||||
}
|
||||
signatureSlice := signature.GetSignature()
|
||||
signatureBytes := [64]byte{}
|
||||
for i := range signatureBytes {
|
||||
signatureBytes[i] = signatureSlice[i]
|
||||
}
|
||||
|
||||
claimAddress, err := address.ValidateAddress(claimAddress)
|
||||
if err != nil {
|
||||
return false, errors.New("invalid address")
|
||||
}
|
||||
|
||||
serializedNoSig, err := claim.SerializedNoSignature()
|
||||
if err != nil {
|
||||
return false, errors.New("serialization error")
|
||||
}
|
||||
|
||||
claimDigest := GetClaimSignatureDigest(claimAddress, certificateId, serializedNoSig)
|
||||
return claim.VerifyDigest(certificate, signatureBytes, claimDigest), nil
|
||||
}
|
||||
|
||||
func (claim *ClaimHelper) ValidateClaimSignature(certificate *ClaimHelper, claimAddress string, certificateId string) (bool, error) {
|
||||
addressBytes, err := address.DecodeAddress(claimAddress)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
certificateIdSlice, err := hex.DecodeString(certificateId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
certificateIdBytes := [20]byte{}
|
||||
for i := range certificateIdBytes {
|
||||
certificateIdBytes[i] = certificateIdSlice[i]
|
||||
}
|
||||
return claim.ValidateClaimSignatureBytes(certificate, addressBytes, certificateIdBytes)
|
||||
}
|
||||
|
|
43
claim/validator_test.go
Normal file
43
claim/validator_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package claim
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateClaimSignature(t *testing.T) {
|
||||
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
|
||||
signed_claim_hex := "080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
|
||||
|
||||
signed_claim, _ := DecodeClaimHex(signed_claim_hex)
|
||||
cert_claim, _ := DecodeClaimHex(cert_claim_hex)
|
||||
|
||||
claim_addr := "bSkUov7HMWpYBiXackDwRnR5ishhGHvtJt"
|
||||
cert_id := "251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
|
||||
|
||||
result, err := signed_claim.ValidateClaimSignature(cert_claim, claim_addr, cert_id)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if result != true {
|
||||
t.Error("failed to validate signature:", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailToValidateClaimSignature(t *testing.T) {
|
||||
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
|
||||
signed_claim_hex := "080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
|
||||
|
||||
signed_claim, _ := DecodeClaimHex(signed_claim_hex)
|
||||
cert_claim, _ := DecodeClaimHex(cert_claim_hex)
|
||||
|
||||
claim_addr := "bSkUov7HMWpYBiXackDwRnR5ishhGHvtJt"
|
||||
cert_id := "251305ca93d4dbedb50dceb282ebcb7b07b7ac64"
|
||||
|
||||
result, err := signed_claim.ValidateClaimSignature(cert_claim, claim_addr, cert_id)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if result != false {
|
||||
t.Error("failed to validate signature:", result)
|
||||
}
|
||||
}
|
3
test.sh
3
test.sh
|
@ -1,2 +1 @@
|
|||
go build
|
||||
go test ./...
|
||||
go test ./... -v
|
||||
|
|
Loading…
Reference in a new issue