func (fe *FileEntityFetcher) FetchEntity(keyId string) (*openpgp.Entity, error) { f, err := wkfs.Open(fe.File) if err != nil { return nil, fmt.Errorf("jsonsign: FetchEntity: %v", err) } defer f.Close() el, err := openpgp.ReadKeyRing(f) if err != nil { return nil, fmt.Errorf("jsonsign: openpgp.ReadKeyRing of %q: %v", fe.File, err) } for _, e := range el { pubk := &e.PrivateKey.PublicKey if pubk.KeyIdString() != keyId { continue } if e.PrivateKey.Encrypted { if err := fe.decryptEntity(e); err == nil { return e, nil } else { return nil, err } } return e, nil } return nil, fmt.Errorf("jsonsign: entity for keyid %q not found in %q", keyId, fe.File) }
// KeyIdFromRing returns the public keyId contained in the secret // ring file secRing. It expects only one keyId in this secret ring // and returns an error otherwise. func KeyIdFromRing(secRing string) (keyId string, err error) { f, err := wkfs.Open(secRing) if err != nil { return "", fmt.Errorf("Could not open secret ring file %v: %v", secRing, err) } defer f.Close() el, err := openpgp.ReadKeyRing(f) if err != nil { return "", fmt.Errorf("Could not read secret ring file %s: %v", secRing, err) } if len(el) != 1 { return "", fmt.Errorf("Secret ring file %v contained %d identities; expected 1", secRing, len(el)) } ent := el[0] return ent.PrimaryKey.KeyIdShortString(), nil }
func VerifyPublicKeyFile(file, keyid string) (bool, error) { f, err := wkfs.Open(file) if err != nil { return false, err } key, err := openArmoredPublicKeyFile(f) if err != nil { return false, err } keyId := publicKeyId(key) if keyId != strings.ToUpper(keyid) { return false, fmt.Errorf("Key in file %q has id %q; expected %q", file, keyId, keyid) } return true, nil }
// EntityFromSecring returns the openpgp Entity from keyFile that matches keyId. // If empty, keyFile defaults to osutil.SecretRingFile(). func EntityFromSecring(keyId, keyFile string) (*openpgp.Entity, error) { if keyId == "" { return nil, errors.New("empty keyId passed to EntityFromSecring") } keyId = strings.ToUpper(keyId) if keyFile == "" { keyFile = osutil.SecretRingFile() } secring, err := wkfs.Open(keyFile) if err != nil { return nil, fmt.Errorf("jsonsign: failed to open keyring: %v", err) } defer secring.Close() el, err := openpgp.ReadKeyRing(secring) if err != nil { return nil, fmt.Errorf("openpgp.ReadKeyRing of %q: %v", keyFile, err) } var entity *openpgp.Entity for _, e := range el { pk := e.PrivateKey if pk == nil || (pk.KeyIdString() != keyId && pk.KeyIdShortString() != keyId) { continue } entity = e } if entity == nil { found := []string{} for _, e := range el { pk := e.PrivateKey if pk == nil { continue } found = append(found, pk.KeyIdShortString()) } return nil, fmt.Errorf("didn't find a key in %q for keyId %q; other keyIds in file = %v", keyFile, keyId, found) } return entity, nil }
func (sr *SignRequest) Sign() (signedJSON string, err error) { trimmedJSON := strings.TrimRightFunc(sr.UnsignedJSON, unicode.IsSpace) // TODO: make sure these return different things inputfail := func(msg string) (string, error) { return "", errors.New(msg) } execfail := func(msg string) (string, error) { return "", errors.New(msg) } jmap := make(map[string]interface{}) if err := json.Unmarshal([]byte(trimmedJSON), &jmap); err != nil { return inputfail("json parse error") } camliSigner, hasSigner := jmap["camliSigner"] if !hasSigner { return inputfail("json lacks \"camliSigner\" key with public key blobref") } camliSignerStr, _ := camliSigner.(string) signerBlob, ok := blob.Parse(camliSignerStr) if !ok { return inputfail("json \"camliSigner\" key is malformed or unsupported") } pubkeyReader, _, err := sr.Fetcher.Fetch(signerBlob) if err != nil { // TODO: not really either an inputfail or an execfail.. but going // with exec for now. return execfail(fmt.Sprintf("failed to find public key %s: %v", signerBlob.String(), err)) } pubk, err := openArmoredPublicKeyFile(pubkeyReader) pubkeyReader.Close() if err != nil { return execfail(fmt.Sprintf("failed to parse public key from blobref %s: %v", signerBlob.String(), err)) } // This check should be redundant if the above JSON parse succeeded, but // for explicitness... if len(trimmedJSON) == 0 || trimmedJSON[len(trimmedJSON)-1] != '}' { return inputfail("json parameter lacks trailing '}'") } trimmedJSON = trimmedJSON[0 : len(trimmedJSON)-1] // sign it entityFetcher := sr.EntityFetcher if entityFetcher == nil { file := sr.secretRingPath() if file == "" { return "", errors.New("jsonsign: no EntityFetcher, and no secret ring file defined.") } secring, err := wkfs.Open(sr.secretRingPath()) if err != nil { return "", fmt.Errorf("jsonsign: failed to open secret ring file %q: %v", sr.secretRingPath(), err) } secring.Close() // just opened to see if it's readable entityFetcher = &FileEntityFetcher{File: file} } signer, err := entityFetcher.FetchEntity(pubk.KeyIdString()) if err != nil { return "", err } var buf bytes.Buffer err = openpgp.ArmoredDetachSign( &buf, signer, strings.NewReader(trimmedJSON), &packet.Config{Time: func() time.Time { return sr.SignatureTime }}, ) if err != nil { return "", err } output := buf.String() index1 := strings.Index(output, "\n\n") index2 := strings.Index(output, "\n-----") if index1 == -1 || index2 == -1 { return execfail("Failed to parse signature from gpg.") } inner := output[index1+2 : index2] signature := strings.Replace(inner, "\n", "", -1) return fmt.Sprintf("%s,\"camliSig\":\"%s\"}\n", trimmedJSON, signature), nil }
func (c *ConfigParser) open(filename string) (File, error) { if c.Open == nil { return wkfs.Open(filename) } return c.Open(filename) }