This commit is contained in:
Jack Robison 2017-09-12 12:02:30 -04:00
parent eb8f72f0d8
commit c62c175d8b
No known key found for this signature in database
GPG key ID: 284699E7404E3CFF
20 changed files with 474 additions and 24 deletions

30
address/address_test.go Normal file
View file

@ -0,0 +1,30 @@
package address
import "testing"
func TestDecodeAddress(t *testing.T) {
addr := "bUc9gyCJPKu2CBYpTvJ98MdmsLb68utjP6"
correct := [25]byte{85, 174, 41, 64, 245, 110, 91, 239, 43, 208, 32, 73, 115, 20, 70, 204, 83, 199, 3,
206, 210, 176, 194, 188, 193}
result, err := DecodeAddress(addr)
if err != nil {
t.Error(err)
}
if result != correct {
t.Error("Mismatch")
}
}
func TestEncodeAddress(t *testing.T) {
addr := [25]byte{85, 174, 41, 64, 245, 110, 91, 239, 43, 208, 32, 73, 115, 20, 70, 204, 83, 199, 3,
206, 210, 176, 194, 188, 193}
correct := "bUc9gyCJPKu2CBYpTvJ98MdmsLb68utjP6"
result, err := EncodeAddress(addr)
if err != nil {
t.Error(err)
}
if result != correct {
t.Error("Mismatch")
}
}

View file

@ -0,0 +1 @@
package base58

View file

@ -0,0 +1,22 @@
package base58
import (
"math/big"
"errors"
)
var b58_characters = [58]byte {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45,
0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a}
func CharacterIndex(character byte) (*big.Int, error) {
for i := 0; i < 58; i++ {
if b58_characters[i] == character {
return big.NewInt(int64(i)), nil
}
}
return nil, errors.New("invalid character")
}

36
address/base58/decode.go Normal file
View file

@ -0,0 +1,36 @@
package base58
import (
"math/big"
"errors"
)
func DecodeBase58(value string, size int64) ([]byte, error) {
buf := []byte(value)
long_value := big.NewInt(0)
result := make([]byte, size)
for i := int64(len(buf) - 1); i >= 0; i-- {
to_add := big.NewInt(0)
to_add = to_add.Exp(big.NewInt(58), big.NewInt(i), to_add)
c, err := CharacterIndex(buf[int64(len(buf)) - i - 1])
if err != nil {
return result, err
}
to_add = to_add.Mul(c, to_add)
long_value = long_value.Add(to_add, long_value)
}
for i := size - 1; i >= 0; i-- {
m := big.NewInt(0)
long_value, m = long_value.DivMod(long_value, big.NewInt(256), m)
bs := m.Bytes()
if len(bs) == 0 {
bs = append(bs, 0x00)
}
b := byte(bs[0])
result[i] = b
}
if long_value.Int64() != 0 {
return result, errors.New("cannot decode to the given size")
}
return result, nil
}

32
address/base58/encode.go Normal file
View file

@ -0,0 +1,32 @@
package base58
import (
"math/big"
)
func EncodeBase58(data []byte) (string) {
long_value := big.NewInt(0)
result := ""
for i := 0; i < len(data); i++ {
to_add := big.NewInt(0)
to_add = to_add.Exp(big.NewInt(256), big.NewInt(int64(i)), to_add)
to_add = to_add.Mul(big.NewInt(int64(data[24 - i])), to_add)
long_value = long_value.Add(to_add, long_value)
}
i := 0
for {
m := big.NewInt(0)
long_value, m = long_value.DivMod(long_value, big.NewInt(58), m)
bs := m.Bytes()
if len(bs) == 0 {
bs = append(bs, 0x00)
}
b := b58_characters[bs[0]]
result = string(b) + result
if long_value.Int64() == 0 {
break
}
i += 1
}
return result
}

18
address/decode.go Normal file
View file

@ -0,0 +1,18 @@
package address
import (
"errors"
"./base58"
)
func DecodeAddress(address string) ([address_length]byte, error) {
decoded, err := base58.DecodeBase58(address, address_length)
if err != nil {
return [address_length]byte{}, errors.New("failed to decode")
}
buf := [address_length]byte{}
for i, b := range decoded {
buf[i] = b
}
return ValidateAddress(buf)
}

13
address/encode.go Normal file
View file

@ -0,0 +1,13 @@
package address
import (
"./base58"
)
func EncodeAddress(address [address_length]byte) (string, error) {
buf, err := ValidateAddress(address)
if err != nil {
return "", err
}
return base58.EncodeBase58(buf[:]), nil
}

60
address/validate.go Normal file
View file

@ -0,0 +1,60 @@
package address
import (
"errors"
"crypto/sha256"
)
const pubkey_prefix = byte(85)
const script_prefix = byte(122)
const prefix_length = 1
const pubkey_length = 20
const checksum_length = 4
const address_length = prefix_length + pubkey_length + checksum_length
var address_prefixes = [2]byte {pubkey_prefix, script_prefix}
func PrefixIsValid(address [address_length]byte) bool {
prefix := address[0]
for _, addr_prefix := range address_prefixes {
if addr_prefix == prefix {
return true
}
}
return false
}
func PubKeyIsValid(address [address_length]byte) bool {
pubkey := address[prefix_length:pubkey_length+prefix_length]
// TODO: validate this for real
if len(pubkey) != pubkey_length {
return false
}
return true
}
func ChecksumIsValid(address [address_length]byte) bool {
checksum := [checksum_length]byte{}
for i := range checksum {checksum[i] = address[prefix_length+pubkey_length+i]}
real_checksum := sha256.Sum256(address[:prefix_length+pubkey_length])
real_checksum = sha256.Sum256(real_checksum[:])
for i, c := range checksum {
if c != real_checksum[i] {
return false
}
}
return true
}
func ValidateAddress(address [address_length]byte) ([address_length]byte, error) {
if !PrefixIsValid(address) {
return address, errors.New("invalid prefix")
}
if !PubKeyIsValid(address) {
return address, errors.New("invalid pubkey")
}
if !ChecksumIsValid(address) {
return address, errors.New("invalid address checksum")
}
return address, nil
}

View file

@ -1,9 +1,6 @@
#!/bin/bash #!/bin/bash
set -euxo pipefail set -euxo pipefail
echo "Building protobuf files" echo "Building protobuf files"
rm -rf pb/*.pb.go rm -rf pb/*.pb.go
protoc --go_out=. pb/*.proto protoc --go_out=. pb/*.proto

5
build_and_test.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
set -euxo pipefail
./build.sh
./test.sh

53
claim/claim.go Normal file
View file

@ -0,0 +1,53 @@
package claim
import (
"github.com/golang/protobuf/proto"
"encoding/hex"
"../pb"
"errors"
)
type Claim struct {
protobuf pb.Claim
}
func (claim *Claim) LoadFromBytes(raw_claim []byte) (error) {
if claim.protobuf.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
return nil
}
func (claim *Claim) LoadFromHexString(claim_hex string) (error) {
buf, err := hex.DecodeString(claim_hex)
if err != nil {
return err
}
return claim.LoadFromBytes(buf)
}
func DecodeClaimBytes(serialized []byte) (*Claim, error) {
claim := &Claim{}
err := claim.LoadFromBytes(serialized)
if err != nil {
return nil, err
}
return claim, nil
}
func DecodeClaimHex(serialized string) (*Claim, error) {
claim_bytes, err := hex.DecodeString(serialized)
if err != nil {
return nil, err
}
return DecodeClaimBytes(claim_bytes)
}

50
claim/decode_test.go Normal file
View file

@ -0,0 +1,50 @@
package claim
import (
"testing"
"encoding/hex"
)
var raw_claims = []string{
"08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367",
"080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65",
"080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f676966",
}
func TestDecodeClaims(t *testing.T) {
for _, claim_hex := range(raw_claims) {
claim := Claim{}
err := claim.LoadFromHexString(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)
}
if serialized_hex != claim_hex {
t.Error("failed to re-serialize")
}
}
}
func TestStripSignature(t *testing.T) {
claim_hex := raw_claims[1]
claim := Claim{}
err := claim.LoadFromHexString(claim_hex)
if err != nil {
t.Error(err)
}
no_sig, err := claim.SerializedNoSignature()
if err != nil {
t.Error(err)
}
if hex.EncodeToString(no_sig) != raw_claims[2] {
t.Error("Failed to remove signature")
}
}

10
claim/pretty.go Normal file
View file

@ -0,0 +1,10 @@
package claim
import (
"github.com/golang/protobuf/jsonpb"
)
func (claim *Claim) RenderJSON() (string, error) {
marshaler := jsonpb.Marshaler{}
return marshaler.MarshalToString(&claim.protobuf)
}

44
claim/serialization.go Normal file
View file

@ -0,0 +1,44 @@
package claim
import (
"../pb"
"errors"
"github.com/golang/protobuf/proto"
"encoding/hex"
)
func (claim *Claim) Serialized() ([]byte, error) {
serialized := claim.protobuf.String()
if serialized == "" {
return nil, errors.New("not initialized")
}
return proto.Marshal(&claim.protobuf)
}
func (claim *Claim) SerializedHexString() (string, error) {
serialized, err := claim.Serialized()
if err != nil {
return "", err
}
serialized_hex := hex.EncodeToString(serialized)
return serialized_hex, nil
}
func (claim *Claim) SerializedNoSignature() ([]byte, error) {
if claim.protobuf.String() == "" {
return nil, errors.New("not initialized")
}
if claim.protobuf.GetPublisherSignature() == nil {
serialized, err := claim.Serialized()
if err != nil {
return nil, err
}
return serialized, nil
} else {
clone := &pb.Claim{}
proto.Merge(clone, &claim.protobuf)
proto.ClearAllExtensions(clone.PublisherSignature)
clone.PublisherSignature = nil
return proto.Marshal(clone)
}
}

View file

@ -0,0 +1,31 @@
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

42
claim/validator.go Normal file
View file

@ -0,0 +1,42 @@
package claim
import (
"../address"
"crypto/sha256"
"errors"
)
func GetClaimSignatureDigest(claim_address [25]byte, certificate_id [20]byte, serialized_no_sig []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)}
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 *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
}

View file

@ -1,21 +0,0 @@
package lbryschema
import (
"fmt"
"github.com/golang/protobuf/proto"
"encoding/hex"
"testing"
"./pb"
)
func TestDecodeCertificate(t *testing.T) {
claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
buf, _ := hex.DecodeString(claim_hex)
testClaim := &pb.Claim{}
proto.Unmarshal(buf, testClaim)
fmt.Println( testClaim)
}
//func TestDecodeAddress(t *testing.T) {
// DecodeAddress("bUc9gyCJPKu2CBYpTvJ98MdmsLb68utjP6")
//}

24
main.go Normal file
View file

@ -0,0 +1,24 @@
package lbryschema_go
import (
"os"
"./claim"
"fmt"
)
func main() {
args := os.Args[1:]
claim_hex := args[0]
decoded, err := claim.DecodeClaimHex(claim_hex)
if err != nil {
fmt.Println("Decoding error: ", err)
return
}
text, err := decoded.RenderJSON()
if err != nil {
fmt.Println("Decoding error: ", err)
return
}
fmt.Println(text)
return
}

1
pb/pb_test.go Normal file
View file

@ -0,0 +1 @@
package pb

2
test.sh Executable file
View file

@ -0,0 +1,2 @@
go build
go test ./...