authPackage.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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 common
  20. import (
  21. "bytes"
  22. "crypto"
  23. "crypto/rand"
  24. "crypto/rsa"
  25. "crypto/sha256"
  26. "crypto/x509"
  27. "encoding/base64"
  28. "encoding/json"
  29. "errors"
  30. )
  31. // AuthenticatedDataPackage is a JSON record containing some Psiphon data
  32. // payload, such as list of Psiphon server entries. As it may be downloaded
  33. // from various sources, it is digitally signed so that the data may be
  34. // authenticated.
  35. type AuthenticatedDataPackage struct {
  36. Data string `json:"data"`
  37. SigningPublicKeyDigest []byte `json:"signingPublicKeyDigest"`
  38. Signature []byte `json:"signature"`
  39. }
  40. // GenerateAuthenticatedDataPackageKeys generates a key pair
  41. // be used to sign and verify AuthenticatedDataPackages.
  42. func GenerateAuthenticatedDataPackageKeys() (string, string, error) {
  43. rsaKey, err := rsa.GenerateKey(rand.Reader, 4096)
  44. if err != nil {
  45. return "", "", ContextError(err)
  46. }
  47. publicKeyBytes, err := x509.MarshalPKIXPublicKey(rsaKey.Public())
  48. if err != nil {
  49. return "", "", ContextError(err)
  50. }
  51. privateKeyBytes := x509.MarshalPKCS1PrivateKey(rsaKey)
  52. return base64.StdEncoding.EncodeToString(publicKeyBytes),
  53. base64.StdEncoding.EncodeToString(privateKeyBytes),
  54. nil
  55. }
  56. func sha256sum(data string) []byte {
  57. hash := sha256.New()
  58. hash.Write([]byte(data))
  59. return hash.Sum(nil)
  60. }
  61. // WriteAuthenticatedDataPackage creates an AuthenticatedDataPackage
  62. // containing the specified data and signed by the given key. The output
  63. // conforms with the legacy format here:
  64. // https://bitbucket.org/psiphon/psiphon-circumvention-system/src/c25d080f6827b141fe637050ce0d5bd0ae2e9db5/Automation/psi_ops_crypto_tools.py
  65. func WriteAuthenticatedDataPackage(
  66. data string, signingPublicKey, signingPrivateKey string) ([]byte, error) {
  67. derEncodedPrivateKey, err := base64.StdEncoding.DecodeString(signingPrivateKey)
  68. if err != nil {
  69. return nil, ContextError(err)
  70. }
  71. rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(derEncodedPrivateKey)
  72. if err != nil {
  73. return nil, ContextError(err)
  74. }
  75. signature, err := rsa.SignPKCS1v15(
  76. rand.Reader,
  77. rsaPrivateKey,
  78. crypto.SHA256,
  79. sha256sum(data))
  80. if err != nil {
  81. return nil, ContextError(err)
  82. }
  83. packageJSON, err := json.Marshal(
  84. &AuthenticatedDataPackage{
  85. Data: data,
  86. SigningPublicKeyDigest: sha256sum(signingPublicKey),
  87. Signature: signature,
  88. })
  89. if err != nil {
  90. return nil, ContextError(err)
  91. }
  92. return packageJSON, nil
  93. }
  94. // ReadAuthenticatedDataPackage extracts and verifies authenticated
  95. // data from an AuthenticatedDataPackage. The package must have been
  96. // signed with the given key.
  97. func ReadAuthenticatedDataPackage(
  98. packageJSON []byte, signingPublicKey string) (string, error) {
  99. var authenticatedDataPackage *AuthenticatedDataPackage
  100. err := json.Unmarshal(packageJSON, &authenticatedDataPackage)
  101. if err != nil {
  102. return "", ContextError(err)
  103. }
  104. derEncodedPublicKey, err := base64.StdEncoding.DecodeString(signingPublicKey)
  105. if err != nil {
  106. return "", ContextError(err)
  107. }
  108. publicKey, err := x509.ParsePKIXPublicKey(derEncodedPublicKey)
  109. if err != nil {
  110. return "", ContextError(err)
  111. }
  112. rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
  113. if !ok {
  114. return "", ContextError(errors.New("unexpected signing public key type"))
  115. }
  116. if 0 != bytes.Compare(
  117. authenticatedDataPackage.SigningPublicKeyDigest,
  118. sha256sum(signingPublicKey)) {
  119. return "", ContextError(errors.New("unexpected signing public key digest"))
  120. }
  121. err = rsa.VerifyPKCS1v15(
  122. rsaPublicKey,
  123. crypto.SHA256,
  124. sha256sum(authenticatedDataPackage.Data),
  125. authenticatedDataPackage.Signature)
  126. if err != nil {
  127. return "", ContextError(err)
  128. }
  129. return authenticatedDataPackage.Data, nil
  130. }