diff --git a/server/go/sigserver/verify.go b/server/go/sigserver/verify.go index 3db188d12..f48f6f7f3 100644 --- a/server/go/sigserver/verify.go +++ b/server/go/sigserver/verify.go @@ -61,6 +61,142 @@ func openArmoredPublicKeyFile(fileName string) (*packet.PublicKeyPacket, os.Erro return &pk, nil } +// See doc/json-signing/* for background and details +// on these variable names. +type VerifyRequest struct { + 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") + } + + log.Printf("camliSig = [%s]", vr.CamliSig) + 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") + } + log.Printf("Got json: %v", pm) + + 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") + } + + log.Printf("Signer: %v", vr.CamliSigner) + return true +} + +func (vr *VerifyRequest) FindAndParsePublicKeyBlob() bool { + // verify that we have the public key signature file + publicKeyFile := fmt.Sprintf("%s/%s.camli", *flagPubKeyDir, vr.CamliSigner.String()) + pk, err := openArmoredPublicKeyFile(publicKeyFile) + if err != nil { + return vr.fail(fmt.Sprintf("Error opening public key file: %v", err)) + } + log.Printf("Public key packet: %v", pk) + vr.PublicKeyPacket = pk + return true; +} + +func (vr *VerifyRequest) VerifySignature() bool { + log.Printf("TODO: implement VerifySignature") + return false +} + +func NewVerificationRequest(sjson string) (vr *VerifyRequest) { + vr = new(VerifyRequest) + vr.ba = []byte(sjson) + + 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:]) + + log.Printf("BP = [%s]", string(vr.bp)) + log.Printf("BPJ = [%s]", string(vr.bpj)) + log.Printf("BS = [%s]", string(vr.bs)) + + if !(vr.ParseSigMap() && + vr.ParsePayloadMap() && + vr.FindAndParsePublicKeyBlob() && + vr.VerifySignature()) { + // 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 + } + return +} + func handleVerify(conn http.ResponseWriter, req *http.Request) { if !(req.Method == "POST" && req.URL.Path == "/camli/sig/verify") { http_util.BadRequestError(conn, "Inconfigured handler.") @@ -83,89 +219,13 @@ func handleVerify(conn http.ResponseWriter, req *http.Request) { return } - // See doc/json-signing/* for background and details - // on these variable names. - - BA := []byte(sjson) - sigIndex := bytes.LastIndex(BA, []byte(sigSeparator)) - if sigIndex == -1 { - verifyFail("no 13-byte camliSig separator found in sjson") + vreq := NewVerificationRequest(sjson) + log.Printf("Request is: %q", vreq) + if vreq.Err != nil { + verifyFail(vreq.Err.String()) return } - // "Bytes Payload" - BP := 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". - BPJ := BA[0:sigIndex+1] - BPJ[sigIndex] = '}' - - BS := []byte("{" + sjson[sigIndex+1:]) - - log.Printf("BP = [%s]", string(BP)) - log.Printf("BPJ = [%s]", string(BPJ)) - log.Printf("BS = [%s]", string(BS)) - - sjsonKeys := make(map[string]interface{}) - if err := json.Unmarshal(BPJ, &sjsonKeys); err != nil { - verifyFail("parse error; JSON is invalid") - return - } - log.Printf("Got json: %v", sjsonKeys) - - if _, hasVersion := sjsonKeys["camliVersion"]; !hasVersion { - verifyFail("Missing 'camliVersion' in the JSON payload") - return - } - - signer, hasSigner := sjsonKeys["camliSigner"] - if !hasSigner { - verifyFail("Missing 'camliSigner' in the JSON payload") - return - } - - if _, ok := signer.(string); !ok { - verifyFail("Invalid 'camliSigner' in the JSON payload") - return - } - - signerBlob := blobref.Parse(signer.(string)) - if signerBlob == nil { - verifyFail("Malformed 'camliSigner' blobref in the JSON payload") - return - } - log.Printf("Signer: %v", signerBlob) - - sigKeyMap := make(map[string]interface{}) - if err := json.Unmarshal(BS, &sigKeyMap); err != nil { - verifyFail("parse error; signature JSON invalid") - return - } - log.Printf("Got sigKeyMap: %v", sigKeyMap) - if len(sigKeyMap) != 1 { - verifyFail("signature JSON didn't have exactly 1 key") - return - } - sigVal, hasCamliSig := sigKeyMap["camliSig"] - if !hasCamliSig { - verifyFail("no 'camliSig' key in signature") - return - } - - log.Printf("sigValu = [%s]", sigVal) - - // verify that we have the public key signature file - publicKeyFile := fmt.Sprintf("%s/%s.camli", *flagPubKeyDir, signerBlob.String()) - - pk, err := openArmoredPublicKeyFile(publicKeyFile) - if err != nil { - verifyFail(fmt.Sprintf("Error opening public key file: %v", err)) - return - } - log.Printf("Public key packet: %v", pk) - log.Printf("TODO: finish implementing") conn.WriteHeader(http.StatusNotImplemented) conn.Write([]byte("TODO: implement"))