/trunk_go/nonce/nonce.go

https://code.google.com/p/nonce-lib/ · Go · 149 lines · 97 code · 17 blank · 35 comment · 24 complexity · 8a23775be266579af11056a0c5048874 MD5 · raw file

  1. // Author: Tyler Egeto, tyleregeto.com
  2. // Free to use, modify, and redistribute.
  3. // A simple nonce implmentation. Nonce, (number used once), is a
  4. // simple way of adding extra security to a web application, particularly
  5. // preventing replay attacks, semantic URL attacks, and also verify the
  6. // source of the request. For more information see these resources:
  7. // a) http://tyleregeto.com/a-guide-to-nonce
  8. // b) http://en.wikipedia.org/wiki/Cryptographic_nonce
  9. package nonce
  10. import (
  11. "fmt"
  12. "time"
  13. "crypto/sha1"
  14. "os"
  15. "io/ioutil"
  16. "strconv"
  17. "log"
  18. )
  19. var (
  20. // Used in every nonce hash generation. It is recommeneded that you
  21. // set this to a value unique to your application.
  22. Salt string = "a-fistfull-of-dollars"
  23. // The max age of a nonce before it expires. In nanoseconds.
  24. // Default value is 24 hours
  25. MaxAge int64 = 1e9 * 60 * 60 * 24;
  26. // The location where expired nonce data is stored
  27. SavePath = "/tmp/"
  28. // Prefix used to identify files created by this package.
  29. filePrefix string = "nonceegeto_"
  30. // How often the GC should run
  31. GcInterval int64 = 1e9 * 60 * 60//1.0 hrs
  32. )
  33. type Nonce struct {
  34. Nonce string
  35. Timestamp int64
  36. }
  37. // Generates a new nonce token based on the arguments & the package
  38. // level salt. Arguments are handled using fmt.Sprintf.
  39. func NewNonce(args ...interface{}) (n *Nonce) {
  40. return makeNonce(time.Nanoseconds(), args)
  41. }
  42. // Recreates a nonce given a timestamp. If the recreated nonce does not match
  43. // the one passed in by the user, it is invalid.
  44. func RecreateNonce(timestamp int64, args ...interface{}) (n *Nonce) {
  45. return makeNonce(timestamp, args)
  46. }
  47. func makeNonce(timestamp int64, args ...interface{}) (n *Nonce) {
  48. str := fmt.Sprint(args...)
  49. str = fmt.Sprintf("a%s7xf6g%s%s9", Salt, str, timestamp)
  50. hash := sha1.New()
  51. hash.Write([]byte(str))
  52. nonce := fmt.Sprintf("%x", hash.Sum())
  53. return &Nonce{Nonce:nonce, Timestamp:timestamp}
  54. }
  55. // Checks if a nonce is valid & can be used.
  56. func IsValid(nonce *Nonce) bool {
  57. //If the time stamp is too old it is expired
  58. diff := time.Nanoseconds() - nonce.Timestamp
  59. if diff > MaxAge {
  60. return false
  61. }
  62. //if a file exists it has already been used
  63. //we expect an error to occur indicating the file doen't exist
  64. _, err := os.Stat(SavePath + filePrefix + nonce.Nonce);
  65. if(err == nil) {
  66. return false;
  67. }
  68. //otherwise it is ok
  69. return true
  70. }
  71. // Expires a nonce value so it cannot be used. Once expired calls to IsValid
  72. // will return false.
  73. func Expire(nonce *Nonce) (e *os.Error) {
  74. file, err := os.Create(SavePath + filePrefix + nonce.Nonce)
  75. if err != nil {
  76. fmt.Println(err.String())
  77. return &err
  78. }
  79. // The comtents of ths file will be its time stamp. GC will use this value.
  80. timestamp := fmt.Sprintf("%d", nonce.Timestamp)
  81. _, err = file.WriteString(timestamp);
  82. if err != nil {
  83. fmt.Println(err.String())
  84. return &err
  85. }
  86. err = file.Close()
  87. return nil
  88. }
  89. // When a nonce is expired files are created to mark them so. `gc` runs
  90. // to clean up these expired files after some time passes.
  91. func gc() {
  92. for {
  93. //grab all the files in the save dir
  94. files, err := ioutil.ReadDir(SavePath)
  95. prefixLen := len(filePrefix)
  96. if err == nil {
  97. for _, fileInfo := range(files) {
  98. //look for files that start with our prefix
  99. name := fileInfo.Name;
  100. if len(name) > prefixLen && name[:prefixLen] == filePrefix {
  101. //the file content is expected to be a timestamp
  102. content, err := ioutil.ReadFile(SavePath + name)
  103. if err != nil {
  104. log.Println(err.String())
  105. continue
  106. }
  107. timestamp, err := strconv.Atoi64(string(content))
  108. if(err == nil) {
  109. diff := time.Nanoseconds() - timestamp
  110. //if the file is old enough delete it
  111. if diff > MaxAge {
  112. err = os.Remove(SavePath + name)
  113. if err != nil {
  114. log.Println(err.String())
  115. }
  116. }
  117. } else {
  118. log.Println(err.String())
  119. }
  120. }
  121. }
  122. } else {
  123. log.Println(err.String())
  124. }
  125. //delay until the next interval
  126. time.Sleep(int64(GcInterval))
  127. }
  128. }
  129. // init starts a goroutine to clean up old files every once in a while.
  130. func init() {
  131. go gc()
  132. }