github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/jsonsign/verify.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package jsonsign 18 19 import ( 20 "bytes" 21 "crypto" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "log" 26 "strings" 27 28 "camlistore.org/pkg/blob" 29 "camlistore.org/pkg/camerrors" 30 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/armor" 31 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/packet" 32 ) 33 34 const sigSeparator = `,"camliSig":"` 35 36 // reArmor takes a camliSig (single line armor) and turns it back into an PGP-style 37 // multi-line armored string 38 func reArmor(line string) string { 39 lastEq := strings.LastIndex(line, "=") 40 if lastEq == -1 { 41 return "" 42 } 43 buf := new(bytes.Buffer) 44 fmt.Fprintf(buf, "-----BEGIN PGP SIGNATURE-----\n\n") 45 payload := line[0:lastEq] 46 crc := line[lastEq:] 47 for len(payload) > 0 { 48 chunkLen := len(payload) 49 if chunkLen > 60 { 50 chunkLen = 60 51 } 52 fmt.Fprintf(buf, "%s\n", payload[0:chunkLen]) 53 payload = payload[chunkLen:] 54 } 55 fmt.Fprintf(buf, "%s\n-----BEGIN PGP SIGNATURE-----\n", crc) 56 return buf.String() 57 } 58 59 // See doc/json-signing/* for background and details 60 // on these variable names. 61 type VerifyRequest struct { 62 fetcher blob.StreamingFetcher // fetcher used to find public key blob 63 64 ba []byte // "bytes all" 65 bp []byte // "bytes payload" (the part that is signed) 66 bpj []byte // "bytes payload, JSON" (BP + "}") 67 bs []byte // "bytes signature", "{" + separator + camliSig, valid JSON 68 69 CamliSigner blob.Ref 70 CamliSig string 71 PublicKeyPacket *packet.PublicKey 72 73 // set if Verify() returns true: 74 PayloadMap map[string]interface{} // The JSON values from BPJ 75 SignerKeyId string // e.g. "2931A67C26F5ABDA" 76 77 Err error // last error encountered 78 } 79 80 func (vr *VerifyRequest) fail(msg string) bool { 81 vr.Err = errors.New("jsonsign: " + msg) 82 return false 83 } 84 85 func (vr *VerifyRequest) ParseSigMap() bool { 86 sigMap := make(map[string]interface{}) 87 if err := json.Unmarshal(vr.bs, &sigMap); err != nil { 88 return vr.fail("invalid JSON in signature") 89 } 90 91 if len(sigMap) != 1 { 92 return vr.fail("signature JSON didn't have exactly 1 key") 93 } 94 95 sigVal, hasCamliSig := sigMap["camliSig"] 96 if !hasCamliSig { 97 return vr.fail("no 'camliSig' key in signature") 98 } 99 100 var ok bool 101 vr.CamliSig, ok = sigVal.(string) 102 if !ok { 103 return vr.fail("camliSig not a string") 104 } 105 106 return true 107 } 108 109 func (vr *VerifyRequest) ParsePayloadMap() bool { 110 vr.PayloadMap = make(map[string]interface{}) 111 pm := vr.PayloadMap 112 113 if err := json.Unmarshal(vr.bpj, &pm); err != nil { 114 return vr.fail("parse error; payload JSON is invalid") 115 } 116 117 if _, hasVersion := pm["camliVersion"]; !hasVersion { 118 return vr.fail("missing 'camliVersion' in the JSON payload") 119 } 120 121 signer, hasSigner := pm["camliSigner"] 122 if !hasSigner { 123 return vr.fail("missing 'camliSigner' in the JSON payload") 124 } 125 126 if _, ok := signer.(string); !ok { 127 return vr.fail("invalid 'camliSigner' in the JSON payload") 128 } 129 130 var ok bool 131 vr.CamliSigner, ok = blob.Parse(signer.(string)) 132 if !ok { 133 return vr.fail("malformed 'camliSigner' blobref in the JSON payload") 134 } 135 return true 136 } 137 138 func (vr *VerifyRequest) FindAndParsePublicKeyBlob() bool { 139 reader, _, err := vr.fetcher.FetchStreaming(vr.CamliSigner) 140 if err != nil { 141 log.Printf("error fetching public key blob %v: %v", vr.CamliSigner, err) 142 // TODO(mpl): we're losing some info here, so maybe 143 // create an error type that contains the reason, 144 // instead of logging the reason. 145 vr.Err = camerrors.ErrMissingKeyBlob 146 return false 147 } 148 defer reader.Close() 149 pk, err := openArmoredPublicKeyFile(reader) 150 if err != nil { 151 return vr.fail(fmt.Sprintf("error opening public key file: %v", err)) 152 } 153 vr.PublicKeyPacket = pk 154 return true 155 } 156 157 func (vr *VerifyRequest) VerifySignature() bool { 158 armorData := reArmor(vr.CamliSig) 159 block, _ := armor.Decode(bytes.NewBufferString(armorData)) 160 if block == nil { 161 return vr.fail("can't parse camliSig armor") 162 } 163 var p packet.Packet 164 var err error 165 p, err = packet.Read(block.Body) 166 if err != nil { 167 return vr.fail("error reading PGP packet from camliSig: " + err.Error()) 168 } 169 sig, ok := p.(*packet.Signature) 170 if !ok { 171 return vr.fail("PGP packet isn't a signature packet") 172 } 173 if sig.Hash != crypto.SHA1 && sig.Hash != crypto.SHA256 { 174 return vr.fail("I can only verify SHA1 or SHA256 signatures") 175 } 176 if sig.SigType != packet.SigTypeBinary { 177 return vr.fail("I can only verify binary signatures") 178 } 179 hash := sig.Hash.New() 180 hash.Write(vr.bp) // payload bytes 181 err = vr.PublicKeyPacket.VerifySignature(hash, sig) 182 if err != nil { 183 return vr.fail(fmt.Sprintf("bad signature: %s", err)) 184 } 185 vr.SignerKeyId = vr.PublicKeyPacket.KeyIdString() 186 return true 187 } 188 189 func NewVerificationRequest(sjson string, fetcher blob.StreamingFetcher) (vr *VerifyRequest) { 190 if fetcher == nil { 191 panic("NewVerificationRequest fetcher is nil") 192 } 193 vr = new(VerifyRequest) 194 vr.ba = []byte(sjson) 195 vr.fetcher = fetcher 196 197 sigIndex := bytes.LastIndex(vr.ba, []byte(sigSeparator)) 198 if sigIndex == -1 { 199 vr.Err = errors.New("jsonsign: no 13-byte camliSig separator found in sjson") 200 return 201 } 202 203 // "Bytes Payload" 204 vr.bp = vr.ba[:sigIndex] 205 206 // "Bytes Payload JSON". Note we re-use the memory (the ",") 207 // from BA in BPJ, so we can't re-use that "," byte for 208 // the opening "{" in "BS". 209 vr.bpj = vr.ba[:sigIndex+1] 210 vr.bpj[sigIndex] = '}' 211 vr.bs = []byte("{" + sjson[sigIndex+1:]) 212 return 213 } 214 215 // TODO: turn this into (bool, os.Error) return, probably, or *Details, os.Error. 216 func (vr *VerifyRequest) Verify() bool { 217 if vr.Err != nil { 218 return false 219 } 220 221 if vr.ParseSigMap() && 222 vr.ParsePayloadMap() && 223 vr.FindAndParsePublicKeyBlob() && 224 vr.VerifySignature() { 225 return true 226 } 227 228 // Don't allow dumbs callers to accidentally check this 229 // if it's not valid. 230 vr.PayloadMap = nil 231 if vr.Err == nil { 232 // The other functions should have filled this in 233 // already, but just in case: 234 vr.Err = errors.New("jsonsign: verification failed") 235 } 236 return false 237 }