fernet.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Package fernet takes a user-provided message (an arbitrary
  2. // sequence of bytes), a key (256 bits), and the current time,
  3. // and produces a token, which contains the message in a form
  4. // that can't be read or altered without the key.
  5. //
  6. // For more information and background, see the Fernet spec
  7. // at https://github.com/fernet/spec.
  8. //
  9. // Subdirectories in this package provide command-line tools
  10. // for working with Fernet keys and tokens.
  11. package fernet
  12. import (
  13. "crypto/aes"
  14. "crypto/cipher"
  15. "crypto/hmac"
  16. "crypto/rand"
  17. "crypto/sha256"
  18. "crypto/subtle"
  19. "encoding/base64"
  20. "encoding/binary"
  21. "io"
  22. "time"
  23. )
  24. const (
  25. version byte = 0x80
  26. tsOffset = 1
  27. ivOffset = tsOffset + 8
  28. payOffset = ivOffset + aes.BlockSize
  29. overhead = 1 + 8 + aes.BlockSize + sha256.Size // ver + ts + iv + hmac
  30. maxClockSkew = 60 * time.Second
  31. )
  32. var encoding = base64.URLEncoding
  33. // generates a token from msg, writes it into tok, and returns the
  34. // number of bytes generated, which is encodedLen(msg).
  35. // len(tok) must be >= encodedLen(len(msg))
  36. func gen(tok, msg, iv []byte, ts time.Time, k *Key) int {
  37. tok[0] = version
  38. binary.BigEndian.PutUint64(tok[tsOffset:], uint64(ts.Unix()))
  39. copy(tok[ivOffset:], iv)
  40. p := tok[payOffset:]
  41. n := pad(p, msg, aes.BlockSize)
  42. bc, _ := aes.NewCipher(k.cryptBytes())
  43. cipher.NewCBCEncrypter(bc, iv).CryptBlocks(p[:n], p[:n])
  44. genhmac(p[n:n], tok[:payOffset+n], k.signBytes())
  45. return payOffset + n + sha256.Size
  46. }
  47. // token length for input msg of length n, not including base64
  48. func encodedLen(n int) int {
  49. const k = aes.BlockSize
  50. return n/k*k + k + overhead
  51. }
  52. // max msg length for tok of length n, for binary token (no base64)
  53. // upper bound; not exact
  54. func decodedLen(n int) int {
  55. return n - overhead
  56. }
  57. // if msg is nil, decrypts in place and returns a slice of tok.
  58. func verify(msg, tok []byte, ttl time.Duration, now time.Time, k *Key) []byte {
  59. if len(tok) < 1 || tok[0] != version {
  60. return nil
  61. }
  62. ts := time.Unix(int64(binary.BigEndian.Uint64(tok[1:])), 0)
  63. if ttl > 0 && (now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew))) {
  64. return nil
  65. }
  66. n := len(tok) - sha256.Size
  67. var hmac [sha256.Size]byte
  68. genhmac(hmac[:0], tok[:n], k.signBytes())
  69. if subtle.ConstantTimeCompare(tok[n:], hmac[:]) != 1 {
  70. return nil
  71. }
  72. pay := tok[payOffset : len(tok)-sha256.Size]
  73. if len(pay)%aes.BlockSize != 0 {
  74. return nil
  75. }
  76. if msg != nil {
  77. copy(msg, pay)
  78. pay = msg
  79. }
  80. bc, _ := aes.NewCipher(k.cryptBytes())
  81. iv := tok[9:][:aes.BlockSize]
  82. cipher.NewCBCDecrypter(bc, iv).CryptBlocks(pay, pay)
  83. return unpad(pay)
  84. }
  85. // Pads p to a multiple of k using PKCS #7 standard block padding.
  86. // See http://tools.ietf.org/html/rfc5652#section-6.3.
  87. func pad(q, p []byte, k int) int {
  88. n := len(p)/k*k + k
  89. copy(q, p)
  90. c := byte(n - len(p))
  91. for i := len(p); i < n; i++ {
  92. q[i] = c
  93. }
  94. return n
  95. }
  96. // Removes PKCS #7 standard block padding from p.
  97. // See http://tools.ietf.org/html/rfc5652#section-6.3.
  98. // This function is the inverse of pad.
  99. // If the padding is not well-formed, unpad returns nil.
  100. func unpad(p []byte) []byte {
  101. c := p[len(p)-1]
  102. for i := len(p) - int(c); i < len(p); i++ {
  103. if i < 0 || p[i] != c {
  104. return nil
  105. }
  106. }
  107. return p[:len(p)-int(c)]
  108. }
  109. func b64enc(src []byte) []byte {
  110. dst := make([]byte, encoding.EncodedLen(len(src)))
  111. encoding.Encode(dst, src)
  112. return dst
  113. }
  114. func b64dec(src []byte) []byte {
  115. dst := make([]byte, encoding.DecodedLen(len(src)))
  116. n, err := encoding.Decode(dst, src)
  117. if err != nil {
  118. return nil
  119. }
  120. return dst[:n]
  121. }
  122. func genhmac(q, p, k []byte) {
  123. h := hmac.New(sha256.New, k)
  124. h.Write(p)
  125. h.Sum(q)
  126. }
  127. // EncryptAndSign encrypts and signs msg with key k and returns the resulting
  128. // fernet token. If msg contains text, the text should be encoded
  129. // with UTF-8 to follow fernet convention.
  130. func EncryptAndSign(msg []byte, k *Key) (tok []byte, err error) {
  131. iv := make([]byte, aes.BlockSize)
  132. if _, err := io.ReadFull(rand.Reader, iv); err != nil {
  133. return nil, err
  134. }
  135. b := make([]byte, encodedLen(len(msg)))
  136. n := gen(b, msg, iv, time.Now(), k)
  137. tok = make([]byte, encoding.EncodedLen(n))
  138. encoding.Encode(tok, b[:n])
  139. return tok, nil
  140. }
  141. // VerifyAndDecrypt verifies that tok is a valid fernet token that was signed
  142. // with a key in k at most ttl time ago only if ttl is greater than zero.
  143. // Returns the message contained in tok if tok is valid, otherwise nil.
  144. func VerifyAndDecrypt(tok []byte, ttl time.Duration, k []*Key) (msg []byte) {
  145. b := make([]byte, encoding.DecodedLen(len(tok)))
  146. n, _ := encoding.Decode(b, tok)
  147. for _, k1 := range k {
  148. msg = verify(nil, b[:n], ttl, time.Now(), k1)
  149. if msg != nil {
  150. return msg
  151. }
  152. }
  153. return nil
  154. }