func AuthCurvePublic(z85SecretKey string) (z85PublicKey string, err error) {
	if minor < 2 {
		return "", ErrorNotImplemented42
	}
	secret := C.CString(z85SecretKey)
	defer C.free(unsafe.Pointer(secret))
	public := C.CString(strings.Repeat(" ", 41))
	defer C.free(unsafe.Pointer(public))
	if i, err := C.zmq4_curve_public(public, secret); int(i) != 0 {
		return "", errget(err)
	}
	z85PublicKey = C.GoString(public)
	return z85PublicKey, nil
}
Helper function to derive z85 public key from secret key

Returns ErrorNotImplemented42 with ZeroMQ version < 4.2
package zmq4
import "C"
import (
	"errors"
	"log"
	"net"
	"strings"
	"sync"
	"unsafe"
)
const CURVE_ALLOW_ANY = "*"
var (
	auth_handler *Socket
	auth_quit    *Socket
	auth_init          = false
	auth_verbose_value = false
	auth_verbose_lock  sync.RWMutex
	auth_allow     = make(map[string]map[string]bool)
	auth_deny      = make(map[string]map[string]bool)
	auth_allow_net = make(map[string][]*net.IPNet)
	auth_deny_net  = make(map[string][]*net.IPNet)
	auth_users = make(map[string]map[string]string)
	auth_pubkeys = make(map[string]map[string]bool)
	auth_meta_handler = auth_meta_handler_default
)
func auth_verbose() bool {
	auth_verbose_lock.RLock()
	value := auth_verbose_value
	auth_verbose_lock.RUnlock()
	return value
}
func auth_verbose_set(value bool) {
	auth_verbose_lock.Lock()
	auth_verbose_value = value
	auth_verbose_lock.Unlock()
}
func auth_meta_handler_default(version, request_id, domain, address, identity, mechanism string, credentials ...string) (metadata map[string]string) {
	return map[string]string{}
}
func auth_isIP(addr string) bool {
	if net.ParseIP(addr) != nil {
		return true
	}
	if _, _, err := net.ParseCIDR(addr); err == nil {
		return true
	}
	return false
}
func auth_is_allowed(domain, address string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_allow[d]; ok {
			if a[address] {
				return true
			}
		}
	}
	addr := net.ParseIP(address)
	if addr != nil {
		for _, d := range []string{domain, "*"} {
			if a, ok := auth_allow_net[d]; ok {
				for _, m := range a {
					if m.Contains(addr) {
						return true
					}
				}
			}
		}
	}
	return false
}
func auth_is_denied(domain, address string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_deny[d]; ok {
			if a[address] {
				return true
			}
		}
	}
	addr := net.ParseIP(address)
	if addr != nil {
		for _, d := range []string{domain, "*"} {
			if a, ok := auth_deny_net[d]; ok {
				for _, m := range a {
					if m.Contains(addr) {
						return true
					}
				}
			}
		}
	}
	return false
}
func auth_has_allow(domain string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_allow[d]; ok {
			if len(a) > 0 || len(auth_allow_net[d]) > 0 {
				return true
			}
		}
	}
	return false
}
func auth_has_deny(domain string) bool {
	for _, d := range []string{domain, "*"} {
		if a, ok := auth_deny[d]; ok {
			if len(a) > 0 || len(auth_deny_net[d]) > 0 {
				return true
			}
		}
	}
	return false
}
func auth_do_handler() {
	for {
		msg, err := auth_handler.RecvMessage(0)
		if err != nil {
			if auth_verbose() {
				log.Println("AUTH: Quitting:", err)
			}
			break
		}
		if msg[0] == "QUIT" {
			if auth_verbose() {
				log.Println("AUTH: Quitting: received QUIT message")
			}
			_, err := auth_handler.SendMessage("QUIT")
			if err != nil && auth_verbose() {
				log.Println("AUTH: Quitting: bouncing QUIT message:", err)
			}
			break
		}
		version := msg[0]
		if version != "1.0" {
			panic("AUTH: version != 1.0")
		}
		request_id := msg[1]
		domain := msg[2]
		address := msg[3]
		identity := msg[4]
		mechanism := msg[5]
		credentials := msg[6:]
		username := ""
		password := ""
		client_key := ""
		if mechanism == "PLAIN" {
			username = msg[6]
			password = msg[7]
		} else if mechanism == "CURVE" {
			s := msg[6]
			if len(s) != 32 {
				panic("AUTH: len(client_key) != 32")
			}
			client_key = Z85encode(s)
		}
		allowed := false
		denied := false
		if auth_has_allow(domain) {
			if auth_is_allowed(domain, address) {
				allowed = true
				if auth_verbose() {
					log.Printf("AUTH: PASSED (whitelist) domain=%q address=%q\n", domain, address)
				}
			} else {
				denied = true
				if auth_verbose() {
					log.Printf("AUTH: DENIED (not in whitelist) domain=%q address=%q\n", domain, address)
				}
			}
		} else if auth_has_deny(domain) {
			if auth_is_denied(domain, address) {
				denied = true
				if auth_verbose() {
					log.Printf("AUTH: DENIED (blacklist) domain=%q address=%q\n", domain, address)
				}
			} else {
				allowed = true
				if auth_verbose() {
					log.Printf("AUTH: PASSED (not in blacklist) domain=%q address=%q\n", domain, address)
				}
			}
		}
		if !denied {
			if mechanism == "NULL" && !allowed {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (NULL)\n")
				}
				allowed = true
			} else if mechanism == "PLAIN" {
				allowed = authenticate_plain(domain, username, password)
			} else if mechanism == "CURVE" {
				allowed = authenticate_curve(domain, client_key)
			}
		}
		if allowed {
			m := auth_meta_handler(version, request_id, domain, address, identity, mechanism, credentials...)
			user_id := ""
			if uid, ok := m["User-Id"]; ok {
				user_id = uid
				delete(m, "User-Id")
			}
			metadata := make([]byte, 0)
			for key, value := range m {
				if len(key) < 256 {
					metadata = append(metadata, auth_meta_blob(key, value)...)
				}
			}
			auth_handler.SendMessage(version, request_id, "200", "OK", user_id, metadata)
		} else {
			auth_handler.SendMessage(version, request_id, "400", "NO ACCESS", "", "")
		}
	}
	err := auth_handler.Close()
	if err != nil && auth_verbose() {
		log.Println("AUTH: Quitting: Close:", err)
	}
	if auth_verbose() {
		log.Println("AUTH: Quit")
	}
}
func authenticate_plain(domain, username, password string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_users[dom]; ok {
			if m[username] == password {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (PLAIN) domain=%q username=%q password=%q\n", dom, username, password)
				}
				return true
			}
		}
	}
	if auth_verbose() {
		log.Printf("AUTH: DENIED (PLAIN) domain=%q username=%q password=%q\n", domain, username, password)
	}
	return false
}
func authenticate_curve(domain, client_key string) bool {
	for _, dom := range []string{domain, "*"} {
		if m, ok := auth_pubkeys[dom]; ok {
			if m[CURVE_ALLOW_ANY] {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (CURVE any client) domain=%q\n", dom)
				}
				return true
			}
			if m[client_key] {
				if auth_verbose() {
					log.Printf("AUTH: ALLOWED (CURVE) domain=%q client_key=%q\n", dom, client_key)
				}
				return true
			}
		}
	}
	if auth_verbose() {
		log.Printf("AUTH: DENIED (CURVE) domain=%q client_key=%q\n", domain, client_key)
	}
	return false
}
func AuthStart() (err error) {
	if auth_init {
		if auth_verbose() {
			log.Println("AUTH: Already running")
		}
		return errors.New("Auth is already running")
	}
	auth_handler, err = NewSocket(REP)
	if err != nil {
		return
	}
	auth_handler.SetLinger(0)
	err = auth_handler.Bind("inproc://zeromq.zap.01")
	if err != nil {
		auth_handler.Close()
		return
	}
	auth_quit, err = NewSocket(REQ)
	if err != nil {
		auth_handler.Close()
		return
	}
	auth_quit.SetLinger(0)
	err = auth_quit.Connect("inproc://zeromq.zap.01")
	if err != nil {
		auth_handler.Close()
		auth_quit.Close()
		return
	}
	go auth_do_handler()
	if auth_verbose() {
		log.Println("AUTH: Starting")
	}
	auth_init = true
	return
}
func AuthStop() {
	if !auth_init {
		if auth_verbose() {
			log.Println("AUTH: Not running, can't stop")
		}
		return
	}
	if auth_verbose() {
		log.Println("AUTH: Stopping")
	}
	_, err := auth_quit.SendMessageDontwait("QUIT")
	if err != nil && auth_verbose() {
		log.Println("AUTH: Stopping: SendMessageDontwait(\"QUIT\"):", err)
	}
	_, err = auth_quit.RecvMessage(0)
	if err != nil && auth_verbose() {
		log.Println("AUTH: Stopping: RecvMessage:", err)
	}
	err = auth_quit.Close()
	if err != nil && auth_verbose() {
		log.Println("AUTH: Stopping: Close:", err)
	}
	if auth_verbose() {
		log.Println("AUTH: Stopped")
	}
	auth_init = false
}
func AuthAllow(domain string, addresses ...string) {
	if auth_isIP(domain) {
		auth_allow_for_domain("*", domain)
		auth_allow_for_domain("*", addresses...)
	} else {
		auth_allow_for_domain(domain, addresses...)
	}
}
func auth_allow_for_domain(domain string, addresses ...string) {
	if _, ok := auth_allow[domain]; !ok {
		auth_allow[domain] = make(map[string]bool)
		auth_allow_net[domain] = make([]*net.IPNet, 0)
	}
	for _, address := range addresses {
		if _, ipnet, err := net.ParseCIDR(address); err == nil {
			auth_allow_net[domain] = append(auth_allow_net[domain], ipnet)
		} else if net.ParseIP(address) != nil {
			auth_allow[domain][address] = true
		} else {
			if auth_verbose() {
				log.Printf("AUTH: Allow for domain %q: %q is not a valid address or network\n", domain, address)
			}
		}
	}
}
func AuthDeny(domain string, addresses ...string) {
	if auth_isIP(domain) {
		auth_deny_for_domain("*", domain)
		auth_deny_for_domain("*", addresses...)
	} else {
		auth_deny_for_domain(domain, addresses...)
	}
}
func auth_deny_for_domain(domain string, addresses ...string) {
	if _, ok := auth_deny[domain]; !ok {
		auth_deny[domain] = make(map[string]bool)
		auth_deny_net[domain] = make([]*net.IPNet, 0)
	}
	for _, address := range addresses {
		if _, ipnet, err := net.ParseCIDR(address); err == nil {
			auth_deny_net[domain] = append(auth_deny_net[domain], ipnet)
		} else if net.ParseIP(address) != nil {
			auth_deny[domain][address] = true
		} else {
			if auth_verbose() {
				log.Printf("AUTH: Deny for domain %q: %q is not a valid address or network\n", domain, address)
			}
		}
	}
}
func AuthPlainAdd(domain, username, password string) {
	if _, ok := auth_users[domain]; !ok {
		auth_users[domain] = make(map[string]string)
	}
	auth_users[domain][username] = password
}
func AuthPlainRemove(domain string, usernames ...string) {
	if u, ok := auth_users[domain]; ok {
		for _, username := range usernames {
			delete(u, username)
		}
	}
}
func AuthPlainRemoveAll(domain string) {
	delete(auth_users, domain)
}
func AuthCurveAdd(domain string, pubkeys ...string) {
	if _, ok := auth_pubkeys[domain]; !ok {
		auth_pubkeys[domain] = make(map[string]bool)
	}
	for _, key := range pubkeys {
		auth_pubkeys[domain][key] = true
	}
}
func AuthCurveRemove(domain string, pubkeys ...string) {
	if p, ok := auth_pubkeys[domain]; ok {
		for _, pubkey := range pubkeys {
			delete(p, pubkey)
		}
	}
}
func AuthCurveRemoveAll(domain string) {
	delete(auth_pubkeys, domain)
}
func AuthSetVerbose(verbose bool) {
	auth_verbose_set(verbose)
}
func AuthSetMetadataHandler(
	handler func(
		version, request_id, domain, address, identity, mechanism string, credentials ...string) (metadata map[string]string)) {
	auth_meta_handler = handler
}
func AuthMetaBlob(key, value string) (blob []byte, err error) {
	if len(key) > 255 {
		return []byte{}, errors.New("Key too long")
	}
	return auth_meta_blob(key, value), nil
}
func auth_meta_blob(name, value string) []byte {
	l1 := len(name)
	l2 := len(value)
	b := make([]byte, l1+l2+5)
	b[0] = byte(l1)
	b[l1+1] = byte(l2 >> 24 & 255)
	b[l1+2] = byte(l2 >> 16 & 255)
	b[l1+3] = byte(l2 >> 8 & 255)
	b[l1+4] = byte(l2 & 255)
	copy(b[1:], []byte(name))
	copy(b[5+l1:], []byte(value))
	return b
}
func (server *Socket) ServerAuthNull(domain string) error {
	err := server.SetPlainServer(0)
	if err == nil {
		err = server.SetZapDomain(domain)
	}
	return err
}
func (server *Socket) ServerAuthPlain(domain string) error {
	err := server.SetPlainServer(1)
	if err == nil {
		err = server.SetZapDomain(domain)
	}
	return err
}
func (server *Socket) ServerAuthCurve(domain, secret_key string) error {
	err := server.SetCurveServer(1)
	if err == nil {
		err = server.SetCurveSecretkey(secret_key)
	}
	if err == nil {
		err = server.SetZapDomain(domain)
	}
	return err
}
func (client *Socket) ClientAuthPlain(username, password string) error {
	err := client.SetPlainUsername(username)
	if err == nil {
		err = client.SetPlainPassword(password)
	}
	return err
}
func (client *Socket) ClientAuthCurve(server_public_key, client_public_key, client_secret_key string) error {
	err := client.SetCurveServerkey(server_public_key)
	if err == nil {
		err = client.SetCurvePublickey(client_public_key)
	}
	if err == nil {
		client.SetCurveSecretkey(client_secret_key)
	}
	return err
}