gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/signtool/sign.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 // Package signtool offers tooling to sign assertions. 21 package signtool 22 23 import ( 24 "encoding/json" 25 "fmt" 26 "os" 27 28 "gitee.com/mysnapcore/mysnapd/asserts" 29 ) 30 31 var ( 32 Stdout = os.Stdout 33 ) 34 35 // Options specifies the complete input for signing an assertion. 36 type Options struct { 37 // KeyID specifies the key id of the key to use 38 KeyID string 39 40 // Statement is used as input to construct the assertion 41 // it's a mapping encoded as JSON 42 // of the header fields of the assertion 43 // plus an optional pseudo-header "body" to specify 44 // the body of the assertion 45 Statement []byte 46 47 // Complement specifies complementary headers to what is in 48 // Statement, for use by tools that fill-in/compute some of 49 // the headers. Headers appearing both in Statement and 50 // Complement are an error, except for "type" that needs 51 // instead to match if present. Pseudo-header "body" can also 52 // be specified here. 53 Complement map[string]interface{} 54 } 55 56 // Sign produces the text of a signed assertion as specified by opts. 57 func Sign(opts *Options, keypairMgr asserts.KeypairManager) ([]byte, error) { 58 var headers map[string]interface{} 59 err := json.Unmarshal(opts.Statement, &headers) 60 if err != nil { 61 return nil, fmt.Errorf("cannot parse the assertion input as JSON: %v", err) 62 } 63 64 for name, value := range opts.Complement { 65 if v, ok := headers[name]; ok { 66 if name == "type" { 67 if v != value { 68 return nil, fmt.Errorf("repeated assertion type does not match") 69 } 70 } else { 71 return nil, fmt.Errorf("complementary header %q clashes with assertion input", name) 72 } 73 } 74 headers[name] = value 75 } 76 77 typCand, ok := headers["type"] 78 if !ok { 79 return nil, fmt.Errorf("missing assertion type header") 80 } 81 typStr, ok := typCand.(string) 82 if !ok { 83 return nil, fmt.Errorf("assertion type must be a string, not: %v", typCand) 84 } 85 typ := asserts.Type(typStr) 86 if typ == nil { 87 return nil, fmt.Errorf("invalid assertion type: %v", headers["type"]) 88 } 89 90 var body []byte 91 if bodyCand, ok := headers["body"]; ok { 92 bodyStr, ok := bodyCand.(string) 93 if !ok { 94 return nil, fmt.Errorf("body if specified must be a string") 95 } 96 body = []byte(bodyStr) 97 delete(headers, "body") 98 } 99 100 adb, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 101 KeypairManager: keypairMgr, 102 }) 103 if err != nil { 104 return nil, err 105 } 106 107 // TODO: teach Sign to cross check keyID and authority-id 108 // against an account-key 109 a, err := adb.Sign(typ, headers, body, opts.KeyID) 110 if err != nil { 111 return nil, err 112 } 113 114 return asserts.Encode(a), nil 115 }