3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-28 23:19:30 +01:00
ergo/irc/utils/crypto.go

98 lines
2.5 KiB
Go
Raw Normal View History

// Copyright (c) 2018 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license
package utils
import (
"crypto/rand"
2020-05-05 04:29:10 +02:00
"crypto/sha256"
"crypto/subtle"
2020-05-05 04:29:10 +02:00
"crypto/tls"
2020-09-23 08:23:35 +02:00
"crypto/x509"
2018-12-28 19:45:55 +01:00
"encoding/base32"
2019-05-12 22:26:23 +02:00
"encoding/base64"
2019-12-29 17:59:49 +01:00
"encoding/hex"
"errors"
2020-05-05 04:29:10 +02:00
"net"
2019-12-18 21:44:06 +01:00
"strings"
2020-05-05 04:29:10 +02:00
"time"
2018-12-28 19:45:55 +01:00
)
var (
// slingamn's own private b32 alphabet, removing 1, l, o, and 0
2019-05-12 08:17:57 +02:00
B32Encoder = base32.NewEncoding("abcdefghijkmnpqrstuvwxyz23456789").WithPadding(base32.NoPadding)
2019-12-29 17:59:49 +01:00
ErrInvalidCertfp = errors.New("Invalid certfp")
2020-05-05 04:29:10 +02:00
ErrNoPeerCerts = errors.New("No certfp available")
ErrNotTLS = errors.New("Connection is not TLS")
)
const (
SecretTokenLength = 26
)
// generate a secret token that cannot be brute-forced via online attacks
func GenerateSecretToken() string {
// 128 bits of entropy are enough to resist any online attack:
var buf [16]byte
rand.Read(buf[:])
2018-12-28 19:45:55 +01:00
// 26 ASCII characters, should be fine for most purposes
2019-05-12 08:17:57 +02:00
return B32Encoder.EncodeToString(buf[:])
}
// securely check if a supplied token matches a stored token
func SecretTokensMatch(storedToken string, suppliedToken string) bool {
// XXX fix a potential gotcha: if the stored token is uninitialized,
// then nothing should match it, not even supplying an empty token.
if len(storedToken) == 0 {
return false
}
return subtle.ConstantTimeCompare([]byte(storedToken), []byte(suppliedToken)) == 1
}
2019-05-12 22:26:23 +02:00
// generate a 256-bit secret key that can be written into a config file
func GenerateSecretKey() string {
var buf [32]byte
rand.Read(buf[:])
return base64.RawURLEncoding.EncodeToString(buf[:])
}
2019-12-18 21:44:06 +01:00
2019-12-29 17:59:49 +01:00
// Normalize openssl-formatted certfp's to oragono's format
func NormalizeCertfp(certfp string) (result string, err error) {
result = strings.ToLower(strings.Replace(certfp, ":", "", -1))
decoded, err := hex.DecodeString(result)
if err != nil || len(decoded) != 32 {
return "", ErrInvalidCertfp
2019-12-18 21:44:06 +01:00
}
2019-12-29 17:59:49 +01:00
return
2019-12-18 21:44:06 +01:00
}
2020-05-05 04:29:10 +02:00
2020-09-23 08:23:35 +02:00
func GetCertFP(conn net.Conn, handshakeTimeout time.Duration) (fingerprint string, peerCerts []*x509.Certificate, err error) {
2020-05-05 04:29:10 +02:00
tlsConn, isTLS := conn.(*tls.Conn)
if !isTLS {
2020-09-23 08:23:35 +02:00
return "", nil, ErrNotTLS
2020-05-05 04:29:10 +02:00
}
// ensure handshake is performed
tlsConn.SetDeadline(time.Now().Add(handshakeTimeout))
err = tlsConn.Handshake()
tlsConn.SetDeadline(time.Time{})
if err != nil {
2020-09-23 08:23:35 +02:00
return "", nil, err
2020-05-05 04:29:10 +02:00
}
2020-09-23 08:23:35 +02:00
peerCerts = tlsConn.ConnectionState().PeerCertificates
2020-05-05 04:29:10 +02:00
if len(peerCerts) < 1 {
2020-09-23 08:23:35 +02:00
return "", nil, ErrNoPeerCerts
2020-05-05 04:29:10 +02:00
}
rawCert := sha256.Sum256(peerCerts[0].Raw)
2020-09-23 08:23:35 +02:00
fingerprint = hex.EncodeToString(rawCert[:])
2020-05-05 04:29:10 +02:00
2020-09-23 08:23:35 +02:00
return fingerprint, peerCerts, nil
2020-05-05 04:29:10 +02:00
}