github.com/decred/politeia@v1.4.0/util/token.go (about) 1 // Copyright (c) 2020-2021 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package util 6 7 import ( 8 "encoding/hex" 9 "fmt" 10 "regexp" 11 12 pdv1 "github.com/decred/politeia/politeiad/api/v1" 13 pdv2 "github.com/decred/politeia/politeiad/api/v2" 14 ) 15 16 var ( 17 // TokenTypeGit represents a token from the politeiad git backend. 18 TokenTypeGit = "git" 19 20 // TokenTypeTstore represents a token from the politeiad tstore 21 // backend. 22 TokenTypeTstore = "tstore" 23 24 // tokenRegexp is a regexp that matches short tokens and full 25 // length tokens. 26 tokenRegexp = regexp.MustCompile(fmt.Sprintf("^[0-9a-f]{%v,%v}$", 27 pdv2.ShortTokenLength, pdv2.TokenSize*2)) 28 ) 29 30 // ShortTokenSize returns the size, in bytes, of a politeiad short token. 31 func ShortTokenSize() int { 32 // If the short token length is an odd number of characters then 33 // padding would have needed to be added to it prior to encoding it 34 // to hex to prevent a hex.ErrLenth (odd length hex string) error. 35 // This function accounts for this padding in the returned size. 36 var size int 37 if pdv1.TokenPrefixLength%2 == 1 { 38 // Add 1 to the length to account for padding 39 size = (pdv2.ShortTokenLength + 1) / 2 40 } else { 41 // No padding was required 42 size = pdv2.ShortTokenLength / 2 43 } 44 return size 45 } 46 47 // ShortToken returns the short version of a token. 48 func ShortToken(token []byte) ([]byte, error) { 49 s := ShortTokenSize() 50 if len(token) < s { 51 return nil, fmt.Errorf("token is not large enough") 52 } 53 return token[:s], nil 54 } 55 56 // ShortTokenString takes a hex encoded token and returns the shortened token 57 // for it. 58 func ShortTokenString(token string) (string, error) { 59 if tokenRegexp.FindString(token) == "" { 60 return "", fmt.Errorf("invalid token %v", tokenRegexp.String()) 61 } 62 return token[:pdv2.ShortTokenLength], nil 63 } 64 65 // ShortTokenEncode returns the hex encoded shortened token. 66 func ShortTokenEncode(token []byte) (string, error) { 67 t, err := ShortToken(token) 68 if err != nil { 69 return "", err 70 } 71 return TokenEncode(t), nil 72 } 73 74 // TokenIsFullLength returns whether a token is a valid, full length politeiad 75 // censorship token. 76 func TokenIsFullLength(tokenType string, token []byte) bool { 77 switch tokenType { 78 case TokenTypeGit: 79 return len(token) == pdv1.TokenSize 80 case TokenTypeTstore: 81 return len(token) == pdv2.TokenSize 82 default: 83 panic("invalid token type") 84 } 85 } 86 87 // TokenDecode decodes a full length token. An error is returned if the token 88 // is not a full length, hex token. 89 func TokenDecode(tokenType, token string) ([]byte, error) { 90 // Verify token is valid 91 if tokenRegexp.FindString(token) == "" { 92 return nil, fmt.Errorf("invalid token %v", tokenRegexp.String()) 93 } 94 95 // Decode token 96 t, err := hex.DecodeString(token) 97 if err != nil { 98 return nil, fmt.Errorf("invalid hex") 99 } 100 101 // Verify token is full length 102 if !TokenIsFullLength(tokenType, t) { 103 return nil, fmt.Errorf("invalid token size") 104 } 105 106 return t, nil 107 } 108 109 // TokenDecodeAnyLength decodes both short tokens and full length tokens. 110 func TokenDecodeAnyLength(tokenType, token string) ([]byte, error) { 111 // Verify token is valid 112 if tokenRegexp.FindString(token) == "" { 113 return nil, fmt.Errorf("invalid token %v", tokenRegexp.String()) 114 } 115 116 // Decode token. If provided token has odd length, add padding 117 // to prevent a hex.ErrLength (odd length hex string) error. 118 if len(token)%2 == 1 { 119 token = token + "0" 120 } 121 t, err := hex.DecodeString(token) 122 if err != nil { 123 return nil, fmt.Errorf("invalid hex") 124 } 125 126 // Verify token byte slice is either a short token or a full length 127 // token. 128 switch { 129 case len(t) == ShortTokenSize(): 130 // This is a short token. Short tokens are the same size 131 // regardless of token type. 132 case tokenType == TokenTypeGit && TokenIsFullLength(TokenTypeGit, t): 133 // Token is a valid git backend token 134 case tokenType == TokenTypeTstore && TokenIsFullLength(TokenTypeTstore, t): 135 // Token is a valid tstore backend token 136 default: 137 return nil, fmt.Errorf("invalid token size") 138 } 139 140 return t, nil 141 } 142 143 // TokenEncode returns the hex encoded token. Its possible that padding has 144 // been added to the token when it was originally decode in order to make it 145 // valid hex. This function checks for padding and removes it before encoding 146 // the token. 147 func TokenEncode(token []byte) string { 148 t := hex.EncodeToString(token) 149 if len(t) == pdv2.ShortTokenLength+1 { 150 // This is a short token that has had padding added to it. Remove 151 // the padding. 152 t = t[:pdv2.ShortTokenLength] 153 } 154 return t 155 } 156 157 // TokenRegexp returns the string regexp that is used to match tokens. 158 func TokenRegexp() string { 159 return tokenRegexp.String() 160 }