accesscontrol.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. * Copyright (c) 2018, 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 accesscontrol implements an access control authorization scheme
  20. // based on digital signatures.
  21. //
  22. // Authorizations for specified access types are issued by an entity that
  23. // digitally signs each authorization. The digital signature is verified
  24. // by service providers before granting the specified access type. Each
  25. // authorization includes an expiry date and a unique ID that may be used
  26. // to mitigate malicious reuse/sharing of authorizations.
  27. //
  28. // In a typical deployment, the signing keys will be present on issuing
  29. // entities which are distinct from service providers. Only verification
  30. // keys will be deployed to service providers.
  31. //
  32. // An authorization is represented in JSON, which is then base64-encoded
  33. // for transport:
  34. //
  35. // {
  36. // "Authorization" : {
  37. // "ID" : <derived unique ID>,
  38. // "AccessType" : <access type name; e.g., "my-access">,
  39. // "Expires" : <RFC3339-encoded UTC time value>
  40. // },
  41. // "SigningKeyID" : <unique key ID>,
  42. // "Signature" : <Ed25519 digital signature>
  43. // }
  44. //
  45. package accesscontrol
  46. import (
  47. "crypto/rand"
  48. "crypto/sha256"
  49. "crypto/subtle"
  50. "encoding/base64"
  51. "encoding/json"
  52. "errors"
  53. "io"
  54. "time"
  55. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  56. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ed25519"
  57. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/hkdf"
  58. )
  59. const (
  60. keyIDLength = 32
  61. authorizationIDKeyLength = 32
  62. authorizationIDLength = 32
  63. )
  64. // SigningKey is the private key used to sign newly issued
  65. // authorizations for the specified access type. The key ID
  66. // is included in authorizations and identifies the
  67. // corresponding verification keys.
  68. //
  69. // AuthorizationIDKey is used to produce a unique
  70. // authentication ID that cannot be mapped back to its seed
  71. // value.
  72. type SigningKey struct {
  73. ID []byte
  74. AccessType string
  75. AuthorizationIDKey []byte
  76. PrivateKey []byte
  77. }
  78. // VerificationKey is the public key used to verify signed
  79. // authentications issued for the specified access type. The
  80. // authorization references the expected public key by ID.
  81. type VerificationKey struct {
  82. ID []byte
  83. AccessType string
  84. PublicKey []byte
  85. }
  86. // NewKeyPair generates a new authorization signing key pair.
  87. func NewKeyPair(
  88. accessType string) (*SigningKey, *VerificationKey, error) {
  89. ID, err := common.MakeSecureRandomBytes(keyIDLength)
  90. if err != nil {
  91. return nil, nil, common.ContextError(err)
  92. }
  93. authorizationIDKey, err := common.MakeSecureRandomBytes(authorizationIDKeyLength)
  94. if err != nil {
  95. return nil, nil, common.ContextError(err)
  96. }
  97. publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
  98. if err != nil {
  99. return nil, nil, common.ContextError(err)
  100. }
  101. signingKey := &SigningKey{
  102. ID: ID,
  103. AccessType: accessType,
  104. AuthorizationIDKey: authorizationIDKey,
  105. PrivateKey: privateKey,
  106. }
  107. verificationKey := &VerificationKey{
  108. ID: ID,
  109. AccessType: accessType,
  110. PublicKey: publicKey,
  111. }
  112. return signingKey, verificationKey, nil
  113. }
  114. // Authorization describes an authorization, with a unique ID,
  115. // granting access to a specified access type, and expiring at
  116. // the specified time.
  117. //
  118. // An Authorization is embedded within a digitally signed
  119. // object. This wrapping object adds a signature and a signing
  120. // key ID.
  121. type Authorization struct {
  122. ID []byte
  123. AccessType string
  124. Expires time.Time
  125. }
  126. type signedAuthorization struct {
  127. Authorization json.RawMessage
  128. SigningKeyID []byte
  129. Signature []byte
  130. }
  131. // ValidateSigningKey checks that a signing key is correctly configured.
  132. func ValidateSigningKey(signingKey *SigningKey) error {
  133. if len(signingKey.ID) != keyIDLength ||
  134. len(signingKey.AccessType) < 1 ||
  135. len(signingKey.AuthorizationIDKey) != authorizationIDKeyLength ||
  136. len(signingKey.PrivateKey) != ed25519.PrivateKeySize {
  137. return common.ContextError(errors.New("invalid signing key"))
  138. }
  139. return nil
  140. }
  141. // IssueAuthorization issues an authorization signed with the
  142. // specified signing key.
  143. //
  144. // seedAuthorizationID should be a value that uniquely identifies
  145. // the purchase, subscription, or transaction that backs the
  146. // authorization; a distinct unique authorization ID will be derived
  147. // from the seed without revealing the original value. The authorization
  148. // ID is to be used to mitigate malicious authorization reuse/sharing.
  149. //
  150. // The return value is a base64-encoded, serialized JSON representation
  151. // of the signed authorization that can be passed to VerifyAuthorization.
  152. func IssueAuthorization(
  153. signingKey *SigningKey,
  154. seedAuthorizationID []byte,
  155. expires time.Time) (string, error) {
  156. err := ValidateSigningKey(signingKey)
  157. if err != nil {
  158. return "", common.ContextError(err)
  159. }
  160. hkdf := hkdf.New(sha256.New, signingKey.AuthorizationIDKey, nil, seedAuthorizationID)
  161. ID := make([]byte, authorizationIDLength)
  162. _, err = io.ReadFull(hkdf, ID)
  163. if err != nil {
  164. return "", common.ContextError(err)
  165. }
  166. auth := Authorization{
  167. ID: ID,
  168. AccessType: signingKey.AccessType,
  169. Expires: expires.UTC(),
  170. }
  171. authJSON, err := json.Marshal(auth)
  172. if err != nil {
  173. return "", common.ContextError(err)
  174. }
  175. signature := ed25519.Sign(signingKey.PrivateKey, authJSON)
  176. signedAuth := signedAuthorization{
  177. Authorization: authJSON,
  178. SigningKeyID: signingKey.ID,
  179. Signature: signature,
  180. }
  181. signedAuthJSON, err := json.Marshal(signedAuth)
  182. if err != nil {
  183. return "", common.ContextError(err)
  184. }
  185. encodedSignedAuth := base64.StdEncoding.EncodeToString(signedAuthJSON)
  186. return encodedSignedAuth, nil
  187. }
  188. // VerificationKeyRing is a set of verification keys to be deployed
  189. // to a service provider for verifying access authorizations.
  190. type VerificationKeyRing struct {
  191. Keys []*VerificationKey
  192. }
  193. // ValidateVerificationKeyRing checks that a verification key ring is
  194. // correctly configured.
  195. func ValidateVerificationKeyRing(keyRing *VerificationKeyRing) error {
  196. for _, key := range keyRing.Keys {
  197. if len(key.ID) != keyIDLength ||
  198. len(key.AccessType) < 1 ||
  199. len(key.PublicKey) != ed25519.PublicKeySize {
  200. return common.ContextError(errors.New("invalid verification key"))
  201. }
  202. }
  203. return nil
  204. }
  205. // VerifyAuthorization verifies the signed authorization and, when
  206. // verified, returns the embedded Authorization struct with the
  207. // access control information.
  208. //
  209. // The key ID in the signed authorization is used to select the
  210. // appropriate verification key from the key ring.
  211. func VerifyAuthorization(
  212. keyRing *VerificationKeyRing,
  213. encodedSignedAuthorization string) (*Authorization, error) {
  214. err := ValidateVerificationKeyRing(keyRing)
  215. if err != nil {
  216. return nil, common.ContextError(err)
  217. }
  218. signedAuthorizationJSON, err := base64.StdEncoding.DecodeString(
  219. encodedSignedAuthorization)
  220. if err != nil {
  221. return nil, common.ContextError(err)
  222. }
  223. var signedAuth signedAuthorization
  224. err = json.Unmarshal(signedAuthorizationJSON, &signedAuth)
  225. if err != nil {
  226. return nil, common.ContextError(err)
  227. }
  228. if len(signedAuth.SigningKeyID) != keyIDLength {
  229. return nil, common.ContextError(errors.New("invalid key ID length"))
  230. }
  231. if len(signedAuth.Signature) != ed25519.SignatureSize {
  232. return nil, common.ContextError(errors.New("invalid signature length"))
  233. }
  234. var verificationKey *VerificationKey
  235. for _, key := range keyRing.Keys {
  236. if subtle.ConstantTimeCompare(signedAuth.SigningKeyID, key.ID) == 1 {
  237. verificationKey = key
  238. }
  239. }
  240. if verificationKey == nil {
  241. return nil, common.ContextError(errors.New("invalid key ID"))
  242. }
  243. if !ed25519.Verify(
  244. verificationKey.PublicKey, signedAuth.Authorization, signedAuth.Signature) {
  245. return nil, common.ContextError(errors.New("invalid signature"))
  246. }
  247. var auth Authorization
  248. err = json.Unmarshal(signedAuth.Authorization, &auth)
  249. if err != nil {
  250. return nil, common.ContextError(err)
  251. }
  252. if len(auth.ID) == 0 {
  253. return nil, common.ContextError(errors.New("invalid authentication ID"))
  254. }
  255. if auth.AccessType != verificationKey.AccessType {
  256. return nil, common.ContextError(errors.New("invalid access type"))
  257. }
  258. if auth.Expires.IsZero() {
  259. return nil, common.ContextError(errors.New("invalid expiry"))
  260. }
  261. if auth.Expires.Before(time.Now().UTC()) {
  262. return nil, common.ContextError(errors.New("expired authentication"))
  263. }
  264. return &auth, nil
  265. }