Clean up and break up verification code

More testable and readable.
This commit is contained in:
Brad Fitzpatrick 2010-12-04 10:12:41 -08:00
parent 5d1ff03248
commit c297269e15
1 changed files with 140 additions and 80 deletions

View File

@ -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"))