Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | model: refactor testimony key introduce TestimonyKey, for store and lookup of testimony introduce Key Identifier (KID) derived from Content Identifier (CID) |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
5c379c0bb19115913099540690cb5497 |
User & Date: | dnc 2019-10-07 01:09:23 |
Context
2019-11-03
| ||
11:58 | model: prefer model.Authority (to string) for type safety check-in: 99cee0289a user: dnc tags: trunk | |
2019-10-07
| ||
01:09 | model: refactor testimony key introduce TestimonyKey, for store and lookup of testimony introduce Key Identifier (KID) derived from Content Identifier (CID) check-in: 5c379c0bb1 user: dnc tags: trunk | |
2019-09-26
| ||
14:28 | minor changes to variable names check-in: ff157b9a48 user: dnc tags: trunk | |
Changes
Changes to model/cid.go.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package model
import (
"crypto/sha256"
"hash"
"github.com/mr-tron/base58"
)
// Content Identifier (encoded, IPFS style)
type CID string
func (this CID) String() string { return string(this) }
// Simple Content Identifier implementation, which is compatible with
// IPFS and IPLD CID's. But to keep things simple, only supporting a
// limited set.
type Sha256CID struct {
hash.Hash
}
func NewSha256CID(data []byte) Sha256CID {
this := Sha256CID{sha256.New()}
if data != nil {
// note that sha256.Sum256() also calls Write() without checking for error
this.Write(data)
}
return this
}
func (this Sha256CID) Bytes() []byte {
//
prefix := []byte{
1, // version (varint)
0x55, // multicodec 0x55 == Raw
18, // multihash 18 == sha2-256
32, // length of sha2-256 sum
}
sum := this.Sum(nil)
return append(prefix, sum...)
}
func (this Sha256CID) String() string {
// `z` for base58btc encoding
return `z` + base58.Encode(this.Bytes())
}
func (this Sha256CID) Encode() CID {
return CID(this.String())
}
|
> > > > > > > > > > > > > > > > > > > > | | | | > > > > |
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
// You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. package model import ( "crypto/sha256" "fmt" "hash" "strings" "github.com/mr-tron/base58" ) // Content Identifier (encoded, IPFS style) type CID string func (this CID) String() string { return string(this) } func (this CID) Decode() ([]byte, error) { if !strings.HasPrefix(string(this), "z") { return nil, fmt.Errorf("unsupported CID encoding (%q)", string(this)) } b, err := base58.Decode(strings.TrimPrefix(string(this), "z")) if err != nil { return nil, fmt.Errorf("bad CID (%q): %w", string(this), err) } if b[0] != 1 || b[1] != 0x55 || b[2] != 18 || b[3] != 32 { return nil, fmt.Errorf("unsupported CID format (%q)", string(this)) } return b[4:], nil } // Simple Content Identifier implementation, which is compatible with // IPFS and IPLD CID's. But to keep things simple, only supporting a // limited set. // A Content Identifier based on Sha256 hash. type Sha256CID struct { hash.Hash } func NewSha256CID(data []byte) Sha256CID { this := Sha256CID{sha256.New()} if data != nil { // note that sha256.Sum256() also calls Write() without checking for error this.Write(data) } return this } // Bytes provides a CID in byte array format. This function returns // prefix bytes followed by the hash sum. Use Sum() to get only the // hash bytes. func (this *Sha256CID) Bytes() []byte { // prefix := []byte{ 1, // version (varint) 0x55, // multicodec 0x55 == Raw 18, // multihash 18 == sha2-256 32, // length of sha2-256 sum } sum := this.Sum() return append(prefix, sum...) } func (this *Sha256CID) String() string { // `z` for base58btc encoding return `z` + base58.Encode(this.Bytes()) } func (this *Sha256CID) Encode() CID { return CID(this.String()) } func (this *Sha256CID) Sum() []byte { return this.Hash.Sum(nil) } |
Changes to model/cid_test.go.
48 49 50 51 52 53 54 55 56 57 58 |
got := cid.String()
if got != want {
t.Errorf("Test: %q, got: %q, wanted %q", v, got, want)
b, err := base58.Decode(want[1:]) // strip leading "z"
if err != nil {
t.Error(err)
}
t.Logf("(hex) Test: %q, got \n\t %x, wanted \n\t %x", v, cid.Sum(nil), b)
}
}
}
|
| |
48 49 50 51 52 53 54 55 56 57 58 |
got := cid.String() if got != want { t.Errorf("Test: %q, got: %q, wanted %q", v, got, want) b, err := base58.Decode(want[1:]) // strip leading "z" if err != nil { t.Error(err) } t.Logf("(hex) Test: %q, got \n\t %x, wanted \n\t %x", v, cid.Sum(), b) } } } |
Changes to model/index.go.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// commands may create and inspect indexes, so we define the common // conventions in this package. package model import ( "encoding/hex" "fmt" "golang.org/x/crypto/ssh" ) func IndexKey(source CID, authority interface{}) (key []string, err error) { var public ssh.PublicKey switch pub := authority.(type) { case string: // use hex of ssh encoding of public key public, _, _, _, err = ssh.ParseAuthorizedKey([]byte(pub)) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
// commands may create and inspect indexes, so we define the common // conventions in this package. package model import ( "encoding/hex" "fmt" "log" "golang.org/x/crypto/ssh" ) // We use Keys to store/lookup content. A Key is really a special // case of Content, so each Key has its own CID. We define a KID type // so that our source code can be explicit and avoid confusion when // working with Keys. type KID CID type Sha256KID Sha256CID func (this *Sha256KID) Sum() []byte { return (*Sha256CID)(this).Sum() } // TestimonyKey refers to tesimony, for lookup and storage in a key/value store or index. type TestimonyKey struct { // Public Key of the signer. Authority Authority `json:"authority"` // Identifies the object of testimony. Content CID `json:"content"` kid *Sha256KID // cache } // KID calculates an IPFS-style content identifier for a testimony key. func (this *TestimonyKey) KID() *Sha256KID { if this.kid != nil { return this.kid } enc, err := Encode(this) if err != nil { log.Panicf("failed to encode %T: %s", this, err) } id := Sha256KID(NewSha256CID(enc)) this.kid = &id return this.kid } func IndexKey(source CID, authority interface{}) (key []string, err error) { var public ssh.PublicKey switch pub := authority.(type) { case string: // use hex of ssh encoding of public key public, _, _, _, err = ssh.ParseAuthorizedKey([]byte(pub)) |
Changes to model/manifest.go.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
_, err := io.Copy(cid, f) if err != nil { return nil, err } return &FileManifest{ Time: time.Now().Unix(), CID: cid.Encode(), }, nil } // Enforce limitations on manifest data. func (this FileManifest) Check() error { const messageCount = 8 const messageLength = 128 |
| |
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
_, err := io.Copy(cid, f)
if err != nil {
return nil, err
}
return &FileManifest{
Time: time.Now().Unix(),
CID: cid.Encode(), // TODO(dnc): avoid unnecessary encodes/decodes, for performance
}, nil
}
// Enforce limitations on manifest data.
func (this FileManifest) Check() error {
const messageCount = 8
const messageLength = 128
|
Changes to model/testimony.go.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
// // Testimony is an attestation by an authority regarding the status of // a source file. package model import ( "errors" "golang.org/x/crypto/ssh" ) // Type Testimony, when produced by a trusted authority, allows a // verifier to authenticate a source file. type Testimony struct { // The signer's public key. Public string `json:"public"` // The encoded bytes that were signed. May be decoded into a manifest. Encoded []byte `json:"encoded"` // Signature of Encoded bytes. Signature ssh.Signature `json:"signature"` ................................................................................ publicKey ssh.PublicKey } // Verify returns nil when the manifest, signature, and public key are // consistent. This checks only the tag data, and does not check that // the key corresponds to an authorized entity. func (this *Testimony) Verify() error { if this.Public == "" { return errors.New("testimony without public key") } public, err := this.PublicKey() if err != nil { return err } err = public.Verify(this.Encoded, &this.Signature) return err } func (this *Testimony) PublicKey() (ssh.PublicKey, error) { var err error if this.publicKey == nil { this.publicKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(this.Public)) } return this.publicKey, err } |
>
>
>
>
|
>
>
>
|
|
>
>
>
>
>
>
>
|
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
..
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
// // Testimony is an attestation by an authority regarding the status of // a source file. package model import ( "errors" "log" "golang.org/x/crypto/ssh" ) // An Authority is public key, encoded as a string (see ssh.ParseAuthorizedKey) type Authority string // Type Testimony, when produced by a trusted authority, allows a // verifier to authenticate a source file. type Testimony struct { // The signer's public key. Authority Authority `json:"authority"` // The data or source file which is the object of attestation. Content CID `json:"content,omitempty"` // The encoded bytes that were signed. May be decoded into a manifest. Encoded []byte `json:"encoded"` // Signature of Encoded bytes. Signature ssh.Signature `json:"signature"` ................................................................................ publicKey ssh.PublicKey } // Verify returns nil when the manifest, signature, and public key are // consistent. This checks only the tag data, and does not check that // the key corresponds to an authorized entity. func (this *Testimony) Verify() error { if this.Authority == "" { return errors.New("testimony without public key") } public, err := this.PublicKey() if err != nil { return err } err = public.Verify(this.Encoded, &this.Signature) return err } func (this *Testimony) PublicKey() (ssh.PublicKey, error) { var err error if this.publicKey == nil { this.publicKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(this.Authority)) } return this.publicKey, err } func (this *Testimony) Key() *TestimonyKey { if this.Authority == "" { log.Panic("testimony not initialized, missing authority") } return &TestimonyKey{Authority: this.Authority, Content: this.Content} } |