utils.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Copyright (c) 2016, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package server
  20. import (
  21. "crypto/rand"
  22. "crypto/rsa"
  23. "crypto/sha1"
  24. "crypto/x509"
  25. "crypto/x509/pkix"
  26. "encoding/pem"
  27. "fmt"
  28. "io"
  29. "math/big"
  30. "sync/atomic"
  31. "time"
  32. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  33. )
  34. // GenerateWebServerCertificate creates a self-signed web server certificate,
  35. // using the specified host name (commonName).
  36. // This is primarily intended for use by MeekServer to generate on-the-fly,
  37. // self-signed TLS certificates for fronted HTTPS mode. In this case, the nature
  38. // of the certificate is non-circumvention; it only has to be acceptable to the
  39. // front CDN making connections to meek.
  40. // The same certificates are used for unfronted HTTPS meek. In this case, the
  41. // certificates may be a fingerprint used to detect Psiphon servers or traffic.
  42. // TODO: more effort to mitigate fingerprinting these certificates.
  43. //
  44. // In addition, GenerateWebServerCertificate is used by GenerateConfig to create
  45. // Psiphon web server certificates for test/example configurations. If these Psiphon
  46. // web server certificates are used in production, the same caveats about
  47. // fingerprints apply.
  48. func GenerateWebServerCertificate(commonName string) (string, string, error) {
  49. // Based on https://golang.org/src/crypto/tls/generate_cert.go
  50. // TODO: use other key types: anti-fingerprint by varying params
  51. rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
  52. if err != nil {
  53. return "", "", common.ContextError(err)
  54. }
  55. // Validity period is ~10 years, starting some number of ~months
  56. // back in the last year.
  57. age, err := common.MakeSecureRandomInt(12)
  58. if err != nil {
  59. return "", "", common.ContextError(err)
  60. }
  61. age += 1
  62. validityPeriod := 10 * 365 * 24 * time.Hour
  63. notBefore := time.Now().Add(time.Duration(-age) * 30 * 24 * time.Hour).UTC()
  64. notAfter := notBefore.Add(validityPeriod).UTC()
  65. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  66. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  67. if err != nil {
  68. return "", "", common.ContextError(err)
  69. }
  70. publicKeyBytes, err := x509.MarshalPKIXPublicKey(rsaKey.Public())
  71. if err != nil {
  72. return "", "", common.ContextError(err)
  73. }
  74. // as per RFC3280 sec. 4.2.1.2
  75. subjectKeyID := sha1.Sum(publicKeyBytes)
  76. var subject pkix.Name
  77. if commonName != "" {
  78. subject = pkix.Name{CommonName: commonName}
  79. }
  80. template := x509.Certificate{
  81. SerialNumber: serialNumber,
  82. Subject: subject,
  83. NotBefore: notBefore,
  84. NotAfter: notAfter,
  85. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  86. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  87. BasicConstraintsValid: true,
  88. IsCA: true,
  89. SubjectKeyId: subjectKeyID[:],
  90. MaxPathLen: 1,
  91. Version: 2,
  92. }
  93. derCert, err := x509.CreateCertificate(
  94. rand.Reader,
  95. &template,
  96. &template,
  97. rsaKey.Public(),
  98. rsaKey)
  99. if err != nil {
  100. return "", "", common.ContextError(err)
  101. }
  102. webServerCertificate := pem.EncodeToMemory(
  103. &pem.Block{
  104. Type: "CERTIFICATE",
  105. Bytes: derCert,
  106. },
  107. )
  108. webServerPrivateKey := pem.EncodeToMemory(
  109. &pem.Block{
  110. Type: "RSA PRIVATE KEY",
  111. Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
  112. },
  113. )
  114. return string(webServerCertificate), string(webServerPrivateKey), nil
  115. }
  116. // IntentionalPanicError is an error type that is used
  117. // when calling panic() in a situation where recovers
  118. // should propagate the panic.
  119. type IntentionalPanicError struct {
  120. message string
  121. }
  122. // NewIntentionalPanicError creates a new IntentionalPanicError.
  123. func NewIntentionalPanicError(errorMessage string) error {
  124. return IntentionalPanicError{
  125. message: fmt.Sprintf("intentional panic error: %s", errorMessage)}
  126. }
  127. // Error implements the error interface.
  128. func (err IntentionalPanicError) Error() string {
  129. return err.message
  130. }
  131. // PanickingLogWriter wraps an io.Writer and intentionally
  132. // panics when a Write() fails.
  133. type PanickingLogWriter struct {
  134. name string
  135. writer io.Writer
  136. }
  137. // NewPanickingLogWriter creates a new PanickingLogWriter.
  138. func NewPanickingLogWriter(
  139. name string, writer io.Writer) *PanickingLogWriter {
  140. return &PanickingLogWriter{
  141. name: name,
  142. writer: writer,
  143. }
  144. }
  145. // Write implements the io.Writer interface.
  146. func (w *PanickingLogWriter) Write(p []byte) (n int, err error) {
  147. n, err = w.writer.Write(p)
  148. if err != nil {
  149. panic(
  150. NewIntentionalPanicError(
  151. fmt.Sprintf("fatal write to %s failed: %s", w.name, err)))
  152. }
  153. return
  154. }
  155. func min(a, b int) int {
  156. if a < b {
  157. return a
  158. }
  159. return b
  160. }
  161. func greaterThanSwapInt64(addr *int64, new int64) bool {
  162. old := atomic.LoadInt64(addr)
  163. if new > old {
  164. return atomic.CompareAndSwapInt64(addr, old, new)
  165. }
  166. return false
  167. }