/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
- // Author: Tyler Egeto, tyleregeto.com
- // Free to use, modify, and redistribute.
- // A simple nonce implmentation. Nonce, (number used once), is a
- // simple way of adding extra security to a web application, particularly
- // preventing replay attacks, semantic URL attacks, and also verify the
- // source of the request. For more information see these resources:
- // a) http://tyleregeto.com/a-guide-to-nonce
- // b) http://en.wikipedia.org/wiki/Cryptographic_nonce
- package nonce
- import (
- "fmt"
- "time"
- "crypto/sha1"
- "os"
- "io/ioutil"
- "strconv"
- "log"
- )
- var (
- // Used in every nonce hash generation. It is recommeneded that you
- // set this to a value unique to your application.
- Salt string = "a-fistfull-of-dollars"
- // The max age of a nonce before it expires. In nanoseconds.
- // Default value is 24 hours
- MaxAge int64 = 1e9 * 60 * 60 * 24;
- // The location where expired nonce data is stored
- SavePath = "/tmp/"
- // Prefix used to identify files created by this package.
- filePrefix string = "nonceegeto_"
- // How often the GC should run
- GcInterval int64 = 1e9 * 60 * 60//1.0 hrs
- )
- type Nonce struct {
- Nonce string
- Timestamp int64
- }
- // Generates a new nonce token based on the arguments & the package
- // level salt. Arguments are handled using fmt.Sprintf.
- func NewNonce(args ...interface{}) (n *Nonce) {
- return makeNonce(time.Nanoseconds(), args)
- }
- // Recreates a nonce given a timestamp. If the recreated nonce does not match
- // the one passed in by the user, it is invalid.
- func RecreateNonce(timestamp int64, args ...interface{}) (n *Nonce) {
- return makeNonce(timestamp, args)
- }
- func makeNonce(timestamp int64, args ...interface{}) (n *Nonce) {
- str := fmt.Sprint(args...)
- str = fmt.Sprintf("a%s7xf6g%s%s9", Salt, str, timestamp)
- hash := sha1.New()
- hash.Write([]byte(str))
- nonce := fmt.Sprintf("%x", hash.Sum())
- return &Nonce{Nonce:nonce, Timestamp:timestamp}
- }
- // Checks if a nonce is valid & can be used.
- func IsValid(nonce *Nonce) bool {
- //If the time stamp is too old it is expired
- diff := time.Nanoseconds() - nonce.Timestamp
- if diff > MaxAge {
- return false
- }
-
- //if a file exists it has already been used
- //we expect an error to occur indicating the file doen't exist
- _, err := os.Stat(SavePath + filePrefix + nonce.Nonce);
- if(err == nil) {
- return false;
- }
-
- //otherwise it is ok
- return true
- }
- // Expires a nonce value so it cannot be used. Once expired calls to IsValid
- // will return false.
- func Expire(nonce *Nonce) (e *os.Error) {
- file, err := os.Create(SavePath + filePrefix + nonce.Nonce)
- if err != nil {
- fmt.Println(err.String())
- return &err
- }
-
- // The comtents of ths file will be its time stamp. GC will use this value.
- timestamp := fmt.Sprintf("%d", nonce.Timestamp)
- _, err = file.WriteString(timestamp);
- if err != nil {
- fmt.Println(err.String())
- return &err
- }
- err = file.Close()
-
- return nil
- }
- // When a nonce is expired files are created to mark them so. `gc` runs
- // to clean up these expired files after some time passes.
- func gc() {
- for {
- //grab all the files in the save dir
- files, err := ioutil.ReadDir(SavePath)
- prefixLen := len(filePrefix)
- if err == nil {
- for _, fileInfo := range(files) {
- //look for files that start with our prefix
- name := fileInfo.Name;
- if len(name) > prefixLen && name[:prefixLen] == filePrefix {
- //the file content is expected to be a timestamp
- content, err := ioutil.ReadFile(SavePath + name)
- if err != nil {
- log.Println(err.String())
- continue
- }
- timestamp, err := strconv.Atoi64(string(content))
- if(err == nil) {
- diff := time.Nanoseconds() - timestamp
- //if the file is old enough delete it
- if diff > MaxAge {
- err = os.Remove(SavePath + name)
- if err != nil {
- log.Println(err.String())
- }
- }
- } else {
- log.Println(err.String())
- }
- }
- }
- } else {
- log.Println(err.String())
- }
- //delay until the next interval
- time.Sleep(int64(GcInterval))
- }
- }
- // init starts a goroutine to clean up old files every once in a while.
- func init() {
- go gc()
- }