package jsonsign import ( "bytes" "camli/blobref" "crypto/openpgp/armor" "crypto/openpgp/packet" "crypto/rsa" "fmt" "json" "log" "os" "strings" "crypto/sha1" ) var logf = log.Printf const sigSeparator = `,"camliSig":"` // reArmor takes a camliSig (single line armor) and turns it back into an PGP-style // multi-line armored string func reArmor(line string) string { lastEq := strings.LastIndex(line, "=") if lastEq == -1 { return "" } return fmt.Sprintf(` -----BEGIN PGP SIGNATURE----- %s %s -----END PGP SIGNATURE----- `, line[0:lastEq], line[lastEq:]) } // See doc/json-signing/* for background and details // on these variable names. type VerifyRequest struct { fetcher blobref.Fetcher // fetcher used to find public key blob ba []byte // "bytes all" bp []byte // "bytes payload" (the part that is signed) bpj []byte // "bytes payload, JSON" (BP + "}") bs []byte // "bytes signature", "{" + separator + camliSig, valid JSON CamliSigner *blobref.BlobRef PayloadMap map[string]interface{} // The JSON values from BPJ CamliSig string PublicKeyPacket *packet.PublicKeyPacket Err os.Error // last error encountered } func (vr *VerifyRequest) fail(msg string) bool { vr.Err = os.NewError(msg) return false } func (vr *VerifyRequest) ParseSigMap() bool { sigMap := make(map[string]interface{}) if err := json.Unmarshal(vr.bs, &sigMap); err != nil { return vr.fail("Invalid JSON in signature") } if len(sigMap) != 1 { return vr.fail("signature JSON didn't have exactly 1 key") } sigVal, hasCamliSig := sigMap["camliSig"] if !hasCamliSig { return vr.fail("no 'camliSig' key in signature") } var ok bool vr.CamliSig, ok = sigVal.(string) if !ok { return vr.fail("camliSig not a string") } return true } func (vr *VerifyRequest) ParsePayloadMap() bool { vr.PayloadMap = make(map[string]interface{}) pm := vr.PayloadMap if err := json.Unmarshal(vr.bpj, &pm); err != nil { return vr.fail("parse error; payload JSON is invalid") } if _, hasVersion := pm["camliVersion"]; !hasVersion { return vr.fail("Missing 'camliVersion' in the JSON payload") } signer, hasSigner := pm["camliSigner"] if !hasSigner { return vr.fail("Missing 'camliSigner' in the JSON payload") } if _, ok := signer.(string); !ok { return vr.fail("Invalid 'camliSigner' in the JSON payload") } vr.CamliSigner = blobref.Parse(signer.(string)) if vr.CamliSigner == nil { return vr.fail("Malformed 'camliSigner' blobref in the JSON payload") } return true } func (vr *VerifyRequest) FindAndParsePublicKeyBlob() bool { reader, _, err := vr.fetcher.Fetch(vr.CamliSigner) if err != nil { return vr.fail(fmt.Sprintf("Error fetching public key blob: %v", err)) } pk, err := openArmoredPublicKeyFile(reader) if err != nil { return vr.fail(fmt.Sprintf("Error opening public key file: %v", err)) } vr.PublicKeyPacket = pk return true; } func (vr *VerifyRequest) VerifySignature() bool { armorData := reArmor(vr.CamliSig) block, _ := armor.Decode([]byte(armorData)) if block == nil { return vr.fail("Can't parse camliSig armor") } buf := bytes.NewBuffer(block.Bytes) p, err := packet.ReadPacket(buf) if err != nil { return vr.fail("Error reading PGP packet from camliSig") } sig, ok := p.(packet.SignaturePacket) if !ok { return vr.fail("PGP packet isn't a signature packet") } if sig.Hash != packet.HashFuncSHA1 { return vr.fail("I can only verify SHA1 signatures") } if sig.SigType != packet.SigTypeBinary { return vr.fail("I can only verify binary signatures") } hash := sha1.New() hash.Write(vr.bp) // payload bytes hash.Write(sig.HashSuffix) hashBytes := hash.Sum() if hashBytes[0] != sig.HashTag[0] || hashBytes[1] != sig.HashTag[1] { return vr.fail("hash tag doesn't match") } err = rsa.VerifyPKCS1v15(&vr.PublicKeyPacket.PublicKey, rsa.HashSHA1, hashBytes, sig.Signature) if err != nil { return vr.fail(fmt.Sprintf("bad signature: %s", err)) } return true } func NewVerificationRequest(sjson string, fetcher blobref.Fetcher) (vr *VerifyRequest) { vr = new(VerifyRequest) vr.ba = []byte(sjson) vr.fetcher = fetcher sigIndex := bytes.LastIndex(vr.ba, []byte(sigSeparator)) if sigIndex == -1 { vr.Err = os.NewError("no 13-byte camliSig separator found in sjson") return } // "Bytes Payload" vr.bp = vr.ba[0:sigIndex] // "Bytes Payload JSON". Note we re-use the memory (the ",") // from BA in BPJ, so we can't re-use that "," byte for // the opening "{" in "BS". vr.bpj = vr.ba[0:sigIndex+1] vr.bpj[sigIndex] = '}' vr.bs = []byte("{" + sjson[sigIndex+1:]) return } func (vr *VerifyRequest) Verify() bool { if vr.Err != nil { return false } if vr.ParseSigMap() && vr.ParsePayloadMap() && vr.FindAndParsePublicKeyBlob() && vr.VerifySignature() { return true; } // Don't allow dumbs callers to accidentally check this // if it's not valid. vr.PayloadMap = nil if vr.Err == nil { // The other functions just fill this in already, // but just in case: vr.Err = os.NewError("Verification failed") } return false }