├── .gitattributes ├── .gitignore ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── chore.md │ ├── feature.md │ ├── question.md │ └── bug.md ├── workflows │ └── build.yaml └── settings.yml ├── go.mod ├── LICENSE ├── go.sum ├── README.md ├── keypair_test.go └── keypair.go /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore dotfiles 2 | .* 3 | !.gitignore 4 | !.gitattributes 5 | !.github 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Why 2 | 3 | > Description 4 | 5 | # What 6 | 7 | * [ ] Closes #0 8 | * [ ] Closes #0 9 | * [ ] Closes #0 10 | 11 | # Notes 12 | 13 | * N/A 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/chore.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: CHORE 3 | about: CHORE issue template 4 | title: "chore: [CHORE NAME]" 5 | labels: types » chore 6 | --- 7 | 8 | # Why 9 | 10 | > Description 11 | 12 | # What 13 | 14 | * [ ] Chore #0 15 | * [ ] Chore #0 16 | * [ ] Chore #0 17 | 18 | # Notes 19 | 20 | * N/A 21 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/proofzero/go-multikeypair 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/mr-tron/base58 v1.2.0 7 | github.com/multiformats/go-varint v0.0.6 8 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 9 | ) 10 | 11 | require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: FEATURE 3 | about: FEATURE issue template 4 | title: "feat: [FEATURE NAME]" 5 | labels: types » feature 6 | --- 7 | 8 | # Why 9 | 10 | > Description 11 | 12 | # What 13 | 14 | * [ ] Feature #0 15 | * [ ] Feature #0 16 | * [ ] Feature #0 17 | 18 | # Notes 19 | 20 | * N/A 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: QUESTION 3 | about: QUESTION issue template 4 | title: "question: [QUESTION NAME]" 5 | labels: forum » topic 6 | --- 7 | 8 | # Why 9 | 10 | > Description 11 | 12 | # What 13 | 14 | * [ ] Question ? 15 | * [ ] Question ? 16 | * [ ] Question ? 17 | 18 | # Notes 19 | 20 | * N/A 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: BUG 3 | about: BUG issue template 4 | title: "bug: [BUG NAME]" 5 | labels: types » bug 6 | --- 7 | 8 | # Why 9 | 10 | > Description 11 | 12 | # How 13 | 14 | ## Steps to Reproduce 15 | 16 | > List steps to reproduce 17 | 18 | ## Expected Behavior 19 | 20 | > Description 21 | 22 | # What 23 | 24 | > Describe the bug 25 | 26 | # Notes 27 | 28 | * N/A 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '*' 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - uses: actions/setup-go@v2 19 | with: 20 | go-version: '^1.17' 21 | - run: go build 22 | - run: go test 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Proof Zero, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 2 | github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 3 | github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= 4 | github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 5 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= 6 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 7 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 8 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 9 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= 10 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 11 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 12 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 13 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 14 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ##### 3 | # https://github.com/probot/settings/blob/master/README.md 4 | _extends: "cloud" 5 | repository: 6 | name: "go-multikeypair" 7 | topics: ["kubelt", "ipld", "ipfs", "data", "data science", "data engineering", "linked data", "crypto", "encryption", "multiformats"] 8 | description: "A multiformat-inspired go module for working with multiple kinds of keypairs." 9 | allow_merge_commits: false 10 | allow_rebase_merge: false 11 | allow_squash_merge: true 12 | archived: false 13 | auto_init: false 14 | delete_branch_on_merge: true 15 | has_downloads: false 16 | has_issues: true 17 | has_wiki: false 18 | is_template: false 19 | visibility: "public" 20 | vulnerability_alerts: true 21 | 22 | branches: 23 | - name: main 24 | protection: 25 | enforce_admins: true 26 | required_signed_commits: false 27 | required_pull_request_reviews: 28 | required_approving_reviews_count: 1 29 | dismiss_stale_reviews: true 30 | require_code_owner_reviews: false 31 | required_status_checks: 32 | strict: true 33 | contexts: [] 34 | restrictions: 35 | apps: [] 36 | users: [] 37 | teams: [] 38 | 39 | labels: 40 | - name: bug 41 | color: CC0000 42 | description: An issue with the system 🐛. 43 | 44 | - name: feature 45 | color: '#336699' 46 | description: New functionality. 47 | 48 | - name: chore 49 | 50 | - name: question 51 | 52 | - name: first-timers-only 53 | 54 | milestones: 55 | - title: alpha 56 | state: open 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-multikeypair 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/proofzero/go-multikeypair.svg)](https://pkg.go.dev/github.com/proofzero/go-multikeypair) 4 | [![Go](https://img.shields.io/github/go-mod/go-version/proofzero/go-multikeypair)](https://golang.org/dl/) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/proofzero/go-multikeypair)](https://goreportcard.com/report/github.com/proofzero/go-multikeypair) 6 | ![build](https://github.com/proofzero/go-multikeypair/actions/workflows/build.yaml/badge.svg) 7 | [![matrix](https://img.shields.io/matrix/lobby:matrix.kubelt.com?label=matrix&server_fqdn=matrix.kubelt.com)](https://matrix.to/#/#lobby:matrix.kubelt.com) 8 | [![Slack](https://img.shields.io/badge/slack-@kubelt-FD4E83.svg)](https://kubelt.slack.com) 9 | 10 | A multiformats-inspired module for encoding cryptographic keypairs. 11 | 12 | # Install 13 | 14 | At a shell within your go module: 15 | 16 | ```bash 17 | go get github.com/proofzero/go-multikeypair 18 | ``` 19 | 20 | # Build Instructions 21 | 22 | ```bash 23 | go build 24 | ``` 25 | 26 | # Testing 27 | 28 | ```bash 29 | go test 30 | ``` 31 | 32 | # Usage 33 | 34 | Pseudo-golang for excercising the Encode and Decode API for a given hardcoded 35 | keypair of a given crypto algorithm: 36 | 37 | ```golang 38 | // Keys: 39 | private := []byte("Wn3Sf5Ke/3:PA:Tm{KCf59Wg6j%/g*#d") 40 | public := []byte("cv-sB6?r*RW8vP5TuMSv_wvw#dV4nUP!@y%u@pmK!P-S2gYVLve!PfdC#kew5Q7U") 41 | 42 | // Cypher: 43 | code := multikeypair.ED_25519 44 | name := multikeypair.Codes[ED_25519] 45 | 46 | // Encode: 47 | mk, err := multikeypair.Encode(private, public, code) 48 | if err != nil { 49 | panic(err) 50 | } 51 | 52 | // Decode: 53 | kp, err := multikeypair.Decode(mk) 54 | if err != nil { 55 | panic(err) 56 | } 57 | ``` 58 | 59 | Documentation is inline with code as comments. See tests in `keypair_test.go`. 60 | 61 | # Contribute 62 | 63 | We would appreciate your help to make this a useful utility. For code contributions, please send a pull request. First outlining your proposed change in an issue or discussion thread to get feedback from other developers is a good idea for anything but small changes. Other ways to contribute include: 64 | - making a feature request 65 | - reporting a bug 66 | - writing a test 67 | - adding some documentation 68 | - providing feedback on the project direction 69 | - reporting your experience using it 70 | 71 | For most things we will use GitHub issues and discussions, but feel free to join the project [Matrix room](https://matrix.to/#/#lobby:matrix.kubelt.com) to chat or ask questions. 72 | -------------------------------------------------------------------------------- /keypair_test.go: -------------------------------------------------------------------------------- 1 | // go-multikeypair/keypair_test.go 2 | 3 | package multikeypair 4 | 5 | import ( 6 | "bytes" 7 | crypto_rand "crypto/rand" 8 | //"fmt" 9 | "testing" 10 | 11 | //auth "golang.org/x/crypto/nacl/auth" 12 | box "golang.org/x/crypto/nacl/box" 13 | sign "golang.org/x/crypto/nacl/sign" 14 | ) 15 | 16 | // Ensure that mapping from code to name, and name to code, are proper 17 | // inverses. 18 | func TestCodes(t *testing.T) { 19 | // identity 20 | if Names[Codes[IDENTITY]] != IDENTITY { 21 | t.Fatal("expected name and code to match for identity") 22 | } 23 | // ed25519 24 | if Names[Codes[ED_25519]] != ED_25519 { 25 | t.Fatal("expected name and code to match for ed25519") 26 | } 27 | } 28 | 29 | // Encode and decode a keypair with string key material. 30 | func TestEncodeFixed(t *testing.T) { 31 | private := []byte("Wn3Sf5Ke/3:PA:Tm{KCf59Wg6j%/g*#d") 32 | public := []byte("cv-sB6?r*RW8vP5TuMSv_wvw#dV4nUP!@y%u@pmK!P-S2gYVLve!PfdC#kew5Q7U") 33 | code := ED_25519 34 | name := Codes[ED_25519] 35 | 36 | mk, err := Encode(private, public, code) 37 | if err != nil { 38 | t.Error(err) 39 | } 40 | kp, err := Decode(mk) 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | 45 | validate(t, kp, code, name, public[:], private[:]) 46 | } 47 | 48 | // Encode and decode a key generated by nacl sign package. 49 | func TestEncodeSign(t *testing.T) { 50 | public, private, err := sign.GenerateKey(crypto_rand.Reader) 51 | if err != nil { 52 | t.Fatal("can't generate key") 53 | } 54 | code := ED_25519 55 | name := Codes[ED_25519] 56 | 57 | mk, err := Encode(private[:], public[:], code) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | kp, err := Decode(mk) 62 | if err != nil { 63 | t.Error(err) 64 | } 65 | 66 | validate(t, kp, code, name, public[:], private[:]) 67 | } 68 | 69 | // Encode and decode a key generated by nacl box package. 70 | func TestEncodeBox(t *testing.T) { 71 | public, private, err := box.GenerateKey(crypto_rand.Reader) 72 | if err != nil { 73 | t.Fatal("can't generate key") 74 | } 75 | code := ED_25519 76 | name := Codes[ED_25519] 77 | mk, err := Encode(private[:], public[:], code) 78 | if err != nil { 79 | t.Error(err) 80 | } 81 | kp, err := Decode(mk) 82 | if err != nil { 83 | t.Error(err) 84 | } 85 | 86 | validate(t, kp, code, name, public[:], private[:]) 87 | } 88 | 89 | // Check that the contents of decoded keypair match the values that were 90 | // used to construct it. 91 | func validate( 92 | t *testing.T, 93 | // A decoded keypair. 94 | kp Keypair, 95 | // The cipher code. 96 | code uint64, 97 | // The cipher name. 98 | name string, 99 | // Public key bytes. 100 | public []byte, 101 | // Private key bytes. 102 | private []byte, 103 | ) { 104 | if kp.Code != code { 105 | t.Errorf("code mismatch after decoding: %d != %d", code, kp.Code) 106 | } 107 | if kp.Name != name { 108 | t.Errorf("name mismatch after decoding: %s != %s", name, kp.Name) 109 | } 110 | if bytes.Compare(private, kp.Private) != 0 { 111 | t.Errorf( 112 | "private key mismatch after decoding: %s != %s", 113 | private, 114 | kp.Private, 115 | ) 116 | } 117 | if len(private) != len(kp.Private) { 118 | t.Errorf( 119 | "private key length mismatch: %d != %d", 120 | len(private), 121 | len(kp.Private), 122 | ) 123 | } 124 | if len(kp.Private) != kp.PrivateLength { 125 | t.Errorf( 126 | "private key length not accurate: %d != %d", 127 | len(kp.Private), 128 | kp.PrivateLength, 129 | ) 130 | } 131 | if bytes.Compare(public, kp.Public) != 0 { 132 | t.Errorf( 133 | "public key mismatch after decoding: %s != %s", 134 | public, 135 | kp.Public, 136 | ) 137 | } 138 | if len(public) != len(kp.Public) { 139 | t.Errorf( 140 | "public key mismatch after decoding: %d != %d", 141 | len(public), 142 | len(kp.Public), 143 | ) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /keypair.go: -------------------------------------------------------------------------------- 1 | // go-multikeypair/keypair.go 2 | // 3 | // Based on IPFS go-multihash, with the aim of being a potential 4 | // addition to the suite of multiformat project types. 5 | 6 | // TODO CLI tool for working with multikeypairs. 7 | // TODO: Investigate cosign/minisign for generating, encoding/decoding? 8 | 9 | package multikeypair 10 | 11 | import ( 12 | "encoding/binary" 13 | "errors" 14 | 15 | //"fmt" 16 | 17 | b58 "github.com/mr-tron/base58/base58" 18 | varint "github.com/multiformats/go-varint" 19 | cryptobyte "golang.org/x/crypto/cryptobyte" 20 | ) 21 | 22 | // Errors 23 | // ----------------------------------------------------------------------------- 24 | 25 | // Minimum and maximum key lengths in bytes. 26 | const ( 27 | MIN_KEY_LENGTH = 2 28 | MAX_KEY_LENGTH = 256 29 | ) 30 | 31 | // Keypair-specific errors this module exports. 32 | var ( 33 | ErrUnknownCode = errors.New("unknown multikeypair code") 34 | ErrTooShort = errors.New("multikeypair too short. must be >= 2 bytes") 35 | ErrTooLong = errors.New("multikeypair too long. must be < 129 bytes") 36 | ErrInvalidMultikeypair = errors.New("input isn't valid multikeypair") 37 | ErrVarintBufferShort = errors.New("uvarint: buffer too small") 38 | ErrVarintTooLong = errors.New("uvarint: varint too big (max 64bit)") 39 | ) 40 | 41 | // Ciphers 42 | // ----------------------------------------------------------------------------- 43 | 44 | // Support ciphers. Accepting PRs for more! 45 | const ( 46 | IDENTITY = uint64(0x00) 47 | ED_25519 = uint64(0x11) 48 | BIP_32 = uint64(0x22) 49 | DSA = uint64(0x33) 50 | RSA = uint64(0x44) 51 | ) 52 | 53 | // Names is a mapping from cipher name to code. 54 | var Names = map[string]uint64{ 55 | "identity": IDENTITY, 56 | "ed25519": ED_25519, 57 | "bip32": BIP_32, 58 | "dsa": DSA, 59 | "res": RSA, 60 | } 61 | 62 | // Codes is a mapping from cipher code to name. 63 | var Codes = map[uint64]string{ 64 | IDENTITY: "identity", 65 | ED_25519: "ed25519", 66 | BIP_32: "bip32", 67 | DSA: "dsa", 68 | RSA: "rsa", 69 | } 70 | 71 | // Keypair 72 | // ----------------------------------------------------------------------------- 73 | 74 | // Keypair is a public/private keypair unpacked into a struct for easy access. 75 | type Keypair struct { 76 | // Cipher identification code. 77 | Code uint64 78 | // Human-readable cipher name. 79 | Name string 80 | // Raw public key bytes. 81 | Public []byte 82 | // Length in bytes of public key. 83 | PublicLength int 84 | // Raw private key bytes. 85 | Private []byte 86 | // Length in bytes of private key. 87 | PrivateLength int 88 | } 89 | 90 | // Multikey 91 | // ----------------------------------------------------------------------------- 92 | 93 | // Multikeypair is a byte slice with the following form: 94 | // [length] (24-bit length prefix) 95 | // [code length] (16-bit length prefix, uvarint code) 96 | // [private key length] (16-bit length prefix) 97 | // [public key length] (16-bit length prefix) 98 | type Multikeypair []byte 99 | 100 | // Implementation 101 | // ----------------------------------------------------------------------------- 102 | 103 | // 104 | // ENCODE 105 | // 106 | 107 | // Encode a keypair into a Multikeypair, specifying the keypair type 108 | // using an integer code. 109 | func Encode(private []byte, public []byte, code uint64) (Multikeypair, error) { 110 | if err := validCode(code); err != nil { 111 | return Multikeypair{}, err 112 | } 113 | b := encodeKeypair(private, public, code) 114 | return Multikeypair(b), nil 115 | } 116 | 117 | // EncodeName encodes a keypair into a Multikeypair, specifying the keypair 118 | // type using a string name instead of an integer code. 119 | func EncodeName(private []byte, public []byte, name string) (Multikeypair, error) { 120 | code := Names[name] 121 | return Encode(private, public, code) 122 | } 123 | 124 | // Encode a Keypair struct into a Multikeypair. 125 | func (k Keypair) Encode() (Multikeypair, error) { 126 | if err := validCode(k.Code); err != nil { 127 | return Multikeypair{}, err 128 | } 129 | return Encode(k.Private, k.Public, k.Code) 130 | } 131 | 132 | // Check that the supplied code is one we recognize. 133 | func validCode(code uint64) error { 134 | for k := range Codes { 135 | if k == code { 136 | return nil 137 | } 138 | } 139 | return ErrUnknownCode 140 | } 141 | 142 | // Pack key material and code type into an array of bytes. 143 | func encodeKeypair(private []byte, public []byte, code uint64) []byte { 144 | codeBuf := PackCode(code) 145 | 146 | var b cryptobyte.Builder 147 | 148 | b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { 149 | // Store the code (packed as varint) with a length prefix. 150 | b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { 151 | b.AddBytes(codeBuf) 152 | }) 153 | // Store the private key with a length prefix. 154 | b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { 155 | b.AddBytes(private) 156 | }) 157 | // Store the public key with a length prefix. 158 | b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { 159 | b.AddBytes(public) 160 | }) 161 | }) 162 | 163 | result, err := b.Bytes() 164 | if err != nil { 165 | panic(err) 166 | } 167 | 168 | return result 169 | } 170 | 171 | // 172 | // DECODE 173 | // 174 | 175 | // Decode unpacks a multikeypair into a Keypair struct. 176 | func Decode(m Multikeypair) (Keypair, error) { 177 | keypair, err := decodeKeypair([]byte(m)) 178 | if err != nil { 179 | return Keypair{}, err 180 | } 181 | 182 | return *keypair, nil 183 | } 184 | 185 | // Decode unpacks a multikeypair into a Keypair struct. 186 | func (m Multikeypair) Decode() (Keypair, error) { 187 | return Decode(m) 188 | } 189 | 190 | func decodeKeypair(buf []byte) (*Keypair, error) { 191 | input := cryptobyte.String(buf) 192 | 193 | // Extract the overall length of the data. 194 | var values cryptobyte.String 195 | if !input.ReadUint24LengthPrefixed(&values) || !input.Empty() { 196 | return nil, ErrInvalidMultikeypair 197 | } 198 | 199 | // Extract the code (packed as a varint) 200 | var code cryptobyte.String 201 | if !values.ReadUint16LengthPrefixed(&code) { 202 | return nil, ErrInvalidMultikeypair 203 | } 204 | // Code is a varint that needs to be unpacked into a uint64. 205 | numCode, err := UnpackCode(code) 206 | if err != nil { 207 | return nil, err 208 | } 209 | 210 | var private cryptobyte.String 211 | if !values.ReadUint16LengthPrefixed(&private) { 212 | return nil, ErrInvalidMultikeypair 213 | } 214 | 215 | var public cryptobyte.String 216 | if !values.ReadUint16LengthPrefixed(&public) { 217 | return nil, ErrInvalidMultikeypair 218 | } 219 | 220 | // Check that the cipher type code we decoded is valid. 221 | if err := validCode(numCode); err != nil { 222 | return nil, err 223 | } 224 | name := Codes[numCode] 225 | privateLength := len(private) 226 | publicLength := len(public) 227 | 228 | keypair := &Keypair{ 229 | Code: numCode, 230 | Name: name, 231 | Private: private, 232 | PrivateLength: privateLength, 233 | Public: public, 234 | PublicLength: publicLength, 235 | } 236 | 237 | return keypair, nil 238 | } 239 | 240 | func castKeypair(buf []byte) (Multikeypair, error) { 241 | _, err := decodeKeypair(buf) 242 | if err != nil { 243 | return Multikeypair{}, err 244 | } 245 | 246 | return Multikeypair(buf), nil 247 | } 248 | 249 | // 250 | // Base-58 251 | // 252 | 253 | // B58String generates a base58-encoded version of a Multikeypair. 254 | func (m Multikeypair) B58String() string { 255 | return b58.Encode([]byte(m)) 256 | } 257 | 258 | // MultikeypairFromB58 parses a base58-encoded hex string into a Multikeypair. 259 | func MultikeypairFromB58(s string) (Multikeypair, error) { 260 | b, err := b58.Decode(s) 261 | if err != nil { 262 | return Multikeypair{}, ErrInvalidMultikeypair 263 | } 264 | 265 | // Test if is valid by attempting to decode as Keypair. 266 | _, err = decodeKeypair(b) 267 | if err != nil { 268 | return Multikeypair{}, err 269 | } 270 | 271 | return castKeypair(b) 272 | } 273 | 274 | // KeypairFromB58 parses a base58-encoded hex string into a Keypair. 275 | func KeypairFromB58(s string) (Keypair, error) { 276 | mk, err := MultikeypairFromB58(s) 277 | if err != nil { 278 | return Keypair{}, err 279 | } 280 | 281 | // Now we have a nominal Multikeypair we can decode into a 282 | // Keypair struct. 283 | kp, err := mk.Decode() 284 | if err != nil { 285 | return Keypair{}, err 286 | } 287 | 288 | return kp, nil 289 | } 290 | 291 | // Utility functions 292 | // ----------------------------------------------------------------------------- 293 | 294 | // PackCode packs a cipher code as varint. 295 | func PackCode(code uint64) []byte { 296 | // Encode a uint64 into a buffer and return number of bytes 297 | // written. Panics if the buffer is too small. 298 | size := varint.UvarintSize(code) 299 | buf := make([]byte, size) 300 | varint.PutUvarint(buf, code) 301 | return buf 302 | } 303 | 304 | // UnpackCode unpacks a varint cipher code. 305 | func UnpackCode(buf []byte) (uint64, error) { 306 | // Returns number of bytes read if successful. On error the 307 | // value is 0 and the of bytes is <= 0, meaning: 308 | // n == 0: buffer too small 309 | // n < 0: value larger than 64 bits (overflow) 310 | code, n := binary.Uvarint(buf) 311 | 312 | if n == 0 { 313 | return 0, ErrVarintBufferShort 314 | } else if n < 0 { 315 | return 0, ErrVarintTooLong 316 | } else { 317 | return code, nil 318 | } 319 | } 320 | --------------------------------------------------------------------------------