2018-09-24 08:00:17 +00:00
package fic
import (
2018-11-16 19:46:19 +00:00
"bytes"
"errors"
"regexp"
2018-09-24 08:00:17 +00:00
"time"
"golang.org/x/crypto/blake2b"
)
// Flag represents a flag's challenge, stored as hash.
type Flag struct {
2018-11-16 19:46:19 +00:00
Id int64 ` json:"id" `
2018-09-24 08:00:17 +00:00
// IdExercice is the identifier of the underlying challenge
2018-11-16 19:46:19 +00:00
IdExercice int64 ` json:"idExercice" `
2018-09-24 08:00:17 +00:00
// Label is the title of the flag as displayed to players
2018-11-16 19:46:19 +00:00
Label string ` json:"label" `
2018-09-24 08:00:17 +00:00
// Help is a small piece of text that aims to add useful information like flag format, ...
2018-11-16 19:46:19 +00:00
Help string ` json:"help" `
2018-09-24 08:00:17 +00:00
// IgnoreCase indicates if the case is sensitive to case or not
2018-11-16 19:46:19 +00:00
IgnoreCase bool ` json:"ignorecase" `
// ValidatorRegexp extracts a subset of the player's answer, that will be checked.
ValidatorRegexp * string ` json:"validator_regexp" `
2018-09-24 08:00:17 +00:00
// Checksum is the expected hashed flag
2018-11-16 19:46:19 +00:00
Checksum [ ] byte ` json:"value" `
2018-12-02 22:18:32 +00:00
// ChoicesCost is the number of points lost to display choices.
ChoicesCost int64 ` json:"choices_cost" `
2018-09-24 08:00:17 +00:00
}
// GetFlags returns a list of flags comming with the challenge.
func ( e Exercice ) GetFlags ( ) ( [ ] Flag , error ) {
2018-12-02 22:18:32 +00:00
if rows , err := DBQuery ( "SELECT id_flag, id_exercice, type, help, ignorecase, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_exercice = ?" , e . Id ) ; err != nil {
2018-09-24 08:00:17 +00:00
return nil , err
} else {
defer rows . Close ( )
var flags = make ( [ ] Flag , 0 )
for rows . Next ( ) {
var k Flag
k . IdExercice = e . Id
2018-12-02 22:18:32 +00:00
if err := rows . Scan ( & k . Id , & k . IdExercice , & k . Label , & k . Help , & k . IgnoreCase , & k . ValidatorRegexp , & k . Checksum , & k . ChoicesCost ) ; err != nil {
2018-09-24 08:00:17 +00:00
return nil , err
}
2018-11-16 19:46:19 +00:00
2018-09-24 08:00:17 +00:00
flags = append ( flags , k )
}
if err := rows . Err ( ) ; err != nil {
return nil , err
}
return flags , nil
}
}
2018-12-02 22:18:32 +00:00
// GetFlag returns a list of flags comming with the challenge.
func GetFlag ( id int64 ) ( k Flag , err error ) {
err = DBQueryRow ( "SELECT id_flag, id_exercice, type, help, ignorecase, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?" , id ) . Scan ( & k . Id , & k . IdExercice , & k . Label , & k . Help , & k . IgnoreCase , & k . ValidatorRegexp , & k . Checksum , & k . ChoicesCost )
return
}
2018-11-28 06:39:50 +00:00
// GetFlagByLabel returns a flag matching the given label.
func ( e Exercice ) GetFlagByLabel ( label string ) ( k Flag , err error ) {
2018-12-02 22:18:32 +00:00
err = DBQueryRow ( "SELECT id_flag, id_exercice, type, help, ignorecase, validator_regexp, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?" , label , e . Id ) . Scan ( & k . Id , & k . IdExercice , & k . Label , & k . Help , & k . IgnoreCase , & k . ValidatorRegexp , & k . Checksum , & k . ChoicesCost )
2018-11-28 06:39:50 +00:00
return
}
2018-09-24 08:00:17 +00:00
// getHashedFlag calculates the expected checksum for the given raw_value.
2018-11-16 19:46:19 +00:00
func getHashedFlag ( raw_value [ ] byte ) [ blake2b . Size ] byte {
hash := blake2b . Sum512 ( raw_value )
2018-09-24 08:00:17 +00:00
return hash
}
// AddRawFlag creates and fills a new struct Flag, from a non-hashed flag, and registers it into the database.
2018-12-02 22:18:32 +00:00
func ( e Exercice ) AddRawFlag ( name string , help string , ignorecase bool , validator_regexp * string , raw_value [ ] byte , choicescost int64 ) ( Flag , error ) {
2018-11-16 19:46:19 +00:00
if ignorecase {
raw_value = bytes . ToLower ( raw_value )
}
// Check that raw value passes through the regexp
if validator_regexp != nil {
if re , err := regexp . Compile ( * validator_regexp ) ; err != nil {
return Flag { } , err
} else if res := re . FindSubmatch ( raw_value ) ; res == nil {
return Flag { } , errors . New ( "Expected flag doesn't pass through the validator_regexp" )
} else {
raw_value = bytes . Join ( res [ 1 : ] , [ ] byte ( "+" ) )
}
}
2018-09-24 08:00:17 +00:00
hash := getHashedFlag ( raw_value )
2018-12-02 22:18:32 +00:00
return e . AddFlag ( name , help , ignorecase , validator_regexp , hash [ : ] , choicescost )
2018-09-24 08:00:17 +00:00
}
// AddFlag creates and fills a new struct Flag, from a hashed flag, and registers it into the database.
2018-12-02 22:18:32 +00:00
func ( e Exercice ) AddFlag ( name string , help string , ignorecase bool , validator_regexp * string , checksum [ ] byte , choicescost int64 ) ( Flag , error ) {
2018-11-16 19:46:19 +00:00
// Check the regexp compile
if validator_regexp != nil {
if _ , err := regexp . Compile ( * validator_regexp ) ; err != nil {
return Flag { } , err
}
}
2018-12-02 22:18:32 +00:00
if res , err := DBExec ( "INSERT INTO exercice_flags (id_exercice, type, help, ignorecase, validator_regexp, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?)" , e . Id , name , help , ignorecase , validator_regexp , checksum , choicescost ) ; err != nil {
2018-09-24 08:00:17 +00:00
return Flag { } , err
} else if kid , err := res . LastInsertId ( ) ; err != nil {
return Flag { } , err
} else {
2018-12-02 22:18:32 +00:00
return Flag { kid , e . Id , name , help , ignorecase , validator_regexp , checksum , choicescost } , nil
2018-09-24 08:00:17 +00:00
}
}
// Update applies modifications back to the database.
func ( k Flag ) Update ( ) ( int64 , error ) {
2018-11-16 19:46:19 +00:00
if k . ValidatorRegexp != nil {
if _ , err := regexp . Compile ( * k . ValidatorRegexp ) ; err != nil {
return 0 , err
}
}
2018-12-02 22:18:32 +00:00
if res , err := DBExec ( "UPDATE exercice_flags SET id_exercice = ?, type = ?, help = ?, ignorecase = ?, validator_regexp = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?" , k . IdExercice , k . Label , k . Help , k . IgnoreCase , k . ValidatorRegexp , k . Checksum , k . ChoicesCost , k . Id ) ; err != nil {
2018-09-24 08:00:17 +00:00
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
// Delete the flag from the database.
func ( k Flag ) Delete ( ) ( int64 , error ) {
if _ , err := DBExec ( "DELETE FROM exercice_files_deps WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
2018-12-02 18:21:07 +00:00
} else if _ , err := DBExec ( "DELETE FROM exercice_flags_deps WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
2018-11-21 03:10:22 +00:00
} else if _ , err := DBExec ( "DELETE FROM flag_choices WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
2018-09-24 08:00:17 +00:00
} else if res , err := DBExec ( "DELETE FROM exercice_flags WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
// WipeFlags deletes flags coming with the challenge.
func ( e Exercice ) WipeFlags ( ) ( int64 , error ) {
if _ , err := DBExec ( "DELETE FROM exercice_files_deps WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)" , e . Id ) ; err != nil {
return 0 , err
2018-12-02 18:21:07 +00:00
} else if _ , err := DBExec ( "DELETE FROM exercice_flags_deps WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)" , e . Id ) ; err != nil {
return 0 , err
2018-12-02 22:18:32 +00:00
} else if _ , err := DBExec ( "DELETE FROM team_wchoices WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)" , e . Id ) ; err != nil {
return 0 , err
2018-11-21 03:10:22 +00:00
} else if _ , err := DBExec ( "DELETE FROM flag_choices WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)" , e . Id ) ; err != nil {
return 0 , err
2018-09-24 08:00:17 +00:00
} else if res , err := DBExec ( "DELETE FROM exercice_flags WHERE id_exercice = ?" , e . Id ) ; err != nil {
return 0 , err
} else if nb , err := res . RowsAffected ( ) ; err != nil {
return 0 , err
} else {
return nb , err
}
}
2018-12-02 18:21:07 +00:00
// AddDepend insert a new dependency to a given flag.
func ( k Flag ) AddDepend ( d Flag ) ( err error ) {
_ , err = DBExec ( "INSERT INTO exercice_flags_deps (id_flag, id_flag_dep) VALUES (?, ?)" , k . Id , d . Id )
return
}
// GetDepends retrieve the flag's dependency list.
func ( k Flag ) GetDepends ( ) ( [ ] Flag , error ) {
if rows , err := DBQuery ( "SELECT id_flag_dep FROM exercice_flags_deps WHERE id_flag = ?" , k . Id ) ; err != nil {
return nil , err
} else {
defer rows . Close ( )
var deps = make ( [ ] Flag , 0 )
for rows . Next ( ) {
var d int64
if err := rows . Scan ( & d ) ; err != nil {
return nil , err
}
2018-12-02 22:18:32 +00:00
deps = append ( deps , Flag { d , k . IdExercice , "" , "" , false , nil , [ ] byte { } , 0 } )
2018-12-02 18:21:07 +00:00
}
if err := rows . Err ( ) ; err != nil {
return nil , err
}
return deps , nil
}
}
2018-09-24 08:00:17 +00:00
// Check if the given val is the expected one for this flag.
2018-11-16 19:46:19 +00:00
func ( k Flag ) Check ( val [ ] byte ) bool {
if k . IgnoreCase {
val = bytes . ToLower ( val )
}
// Check that raw value passes through the regexp
if k . ValidatorRegexp != nil {
re := regexp . MustCompile ( * k . ValidatorRegexp )
if res := re . FindSubmatch ( val ) ; res != nil {
val = bytes . Join ( res [ 1 : ] , [ ] byte ( "+" ) )
}
}
// Check that the value is not empty
if len ( val ) == 0 {
return false
}
2018-09-24 08:00:17 +00:00
hash := getHashedFlag ( val )
if len ( k . Checksum ) != len ( hash ) {
return false
}
for i := range hash {
if k . Checksum [ i ] != hash [ i ] {
return false
}
}
return true
}
// FoundBy registers in the database that the given Team solved the flag.
func ( k Flag ) FoundBy ( t Team ) {
DBExec ( "INSERT INTO flag_found (id_flag, id_team, time) VALUES (?, ?, ?)" , k . Id , t . Id , time . Now ( ) )
}