more
This commit is contained in:
parent
eb8f72f0d8
commit
c62c175d8b
20 changed files with 474 additions and 24 deletions
30
address/address_test.go
Normal file
30
address/address_test.go
Normal 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")
|
||||
}
|
||||
}
|
1
address/base58/base58_test.go
Normal file
1
address/base58/base58_test.go
Normal file
|
@ -0,0 +1 @@
|
|||
package base58
|
22
address/base58/character.go
Normal file
22
address/base58/character.go
Normal 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
36
address/base58/decode.go
Normal 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
32
address/base58/encode.go
Normal 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
18
address/decode.go
Normal 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
13
address/encode.go
Normal 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
60
address/validate.go
Normal 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
|
||||
}
|
3
build.sh
3
build.sh
|
@ -1,9 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
echo "Building protobuf files"
|
||||
rm -rf pb/*.pb.go
|
||||
|
||||
protoc --go_out=. pb/*.proto
|
||||
|
||||
|
|
5
build_and_test.sh
Executable file
5
build_and_test.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
./build.sh
|
||||
./test.sh
|
53
claim/claim.go
Normal file
53
claim/claim.go
Normal 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
50
claim/decode_test.go
Normal 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
10
claim/pretty.go
Normal 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
44
claim/serialization.go
Normal 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)
|
||||
}
|
||||
}
|
31
claim/test_verify_signature.go
Normal file
31
claim/test_verify_signature.go
Normal 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
42
claim/validator.go
Normal 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
|
||||
}
|
|
@ -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
24
main.go
Normal 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
1
pb/pb_test.go
Normal file
|
@ -0,0 +1 @@
|
|||
package pb
|
2
test.sh
Executable file
2
test.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
go build
|
||||
go test ./...
|
Loading…
Add table
Reference in a new issue