utils.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. "time"
  31. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  32. )
  33. // GenerateWebServerCertificate creates a self-signed web server certificate,
  34. // using the specified host name (commonName).
  35. // This is primarily intended for use by MeekServer to generate on-the-fly,
  36. // self-signed TLS certificates for fronted HTTPS mode. In this case, the nature
  37. // of the certificate is non-circumvention; it only has to be acceptable to the
  38. // front CDN making connections to meek.
  39. // The same certificates are used for unfronted HTTPS meek. In this case, the
  40. // certificates may be a fingerprint used to detect Psiphon servers or traffic.
  41. // TODO: more effort to mitigate fingerprinting these certificates.
  42. //
  43. // In addition, GenerateWebServerCertificate is used by GenerateConfig to create
  44. // Psiphon web server certificates for test/example configurations. If these Psiphon
  45. // web server certificates are used in production, the same caveats about
  46. // fingerprints apply.
  47. func GenerateWebServerCertificate(commonName string) (string, string, error) {
  48. // Based on https://golang.org/src/crypto/tls/generate_cert.go
  49. // TODO: use other key types: anti-fingerprint by varying params
  50. rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
  51. if err != nil {
  52. return "", "", common.ContextError(err)
  53. }
  54. // Validity period is ~10 years, starting some number of ~months
  55. // back in the last year.
  56. age, err := common.MakeSecureRandomInt(12)
  57. if err != nil {
  58. return "", "", common.ContextError(err)
  59. }
  60. age += 1
  61. validityPeriod := 10 * 365 * 24 * time.Hour
  62. notBefore := time.Now().Add(time.Duration(-age) * 30 * 24 * time.Hour).UTC()
  63. notAfter := notBefore.Add(validityPeriod).UTC()
  64. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  65. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  66. if err != nil {
  67. return "", "", common.ContextError(err)
  68. }
  69. publicKeyBytes, err := x509.MarshalPKIXPublicKey(rsaKey.Public())
  70. if err != nil {
  71. return "", "", common.ContextError(err)
  72. }
  73. // as per RFC3280 sec. 4.2.1.2
  74. subjectKeyID := sha1.Sum(publicKeyBytes)
  75. var subject pkix.Name
  76. if commonName != "" {
  77. subject = pkix.Name{CommonName: commonName}
  78. }
  79. template := x509.Certificate{
  80. SerialNumber: serialNumber,
  81. Subject: subject,
  82. NotBefore: notBefore,
  83. NotAfter: notAfter,
  84. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  85. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  86. BasicConstraintsValid: true,
  87. IsCA: true,
  88. SubjectKeyId: subjectKeyID[:],
  89. MaxPathLen: 1,
  90. Version: 2,
  91. }
  92. derCert, err := x509.CreateCertificate(
  93. rand.Reader,
  94. &template,
  95. &template,
  96. rsaKey.Public(),
  97. rsaKey)
  98. if err != nil {
  99. return "", "", common.ContextError(err)
  100. }
  101. webServerCertificate := pem.EncodeToMemory(
  102. &pem.Block{
  103. Type: "CERTIFICATE",
  104. Bytes: derCert,
  105. },
  106. )
  107. webServerPrivateKey := pem.EncodeToMemory(
  108. &pem.Block{
  109. Type: "RSA PRIVATE KEY",
  110. Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
  111. },
  112. )
  113. return string(webServerCertificate), string(webServerPrivateKey), nil
  114. }
  115. // IntentionalPanicError is an error type that is used
  116. // when calling panic() in a situation where recovers
  117. // should propagate the panic.
  118. type IntentionalPanicError struct {
  119. message string
  120. }
  121. // NewIntentionalPanicError creates a new IntentionalPanicError.
  122. func NewIntentionalPanicError(errorMessage string) error {
  123. return IntentionalPanicError{
  124. message: fmt.Sprintf("intentional panic error: %s", errorMessage)}
  125. }
  126. // Error implements the error interface.
  127. func (err IntentionalPanicError) Error() string {
  128. return err.message
  129. }
  130. // PanickingLogWriter wraps an io.Writer and intentionally
  131. // panics when a Write() fails.
  132. type PanickingLogWriter struct {
  133. name string
  134. writer io.Writer
  135. }
  136. // NewPanickingLogWriter creates a new PanickingLogWriter.
  137. func NewPanickingLogWriter(
  138. name string, writer io.Writer) *PanickingLogWriter {
  139. return &PanickingLogWriter{
  140. name: name,
  141. writer: writer,
  142. }
  143. }
  144. // Write implements the io.Writer interface.
  145. func (w *PanickingLogWriter) Write(p []byte) (n int, err error) {
  146. n, err = w.writer.Write(p)
  147. if err != nil {
  148. panic(
  149. NewIntentionalPanicError(
  150. fmt.Sprintf("fatal write to %s failed: %s", w.name, err)))
  151. }
  152. return
  153. }