2019-01-02 20:51:09 +00:00
package fic
import (
"bytes"
"errors"
2019-01-16 05:02:06 +00:00
"fmt"
2019-01-02 20:51:09 +00:00
"regexp"
"time"
"golang.org/x/crypto/blake2b"
)
// FlagKey represents a flag's challenge, stored as hash.
type FlagKey struct {
Id int64 ` json:"id" `
// IdExercice is the identifier of the underlying challenge
IdExercice int64 ` json:"idExercice" `
// Label is the title of the flag as displayed to players
Label string ` json:"label" `
// Help is a small piece of text that aims to add useful information like flag format, ...
Help string ` json:"help" `
// IgnoreCase indicates if the case is sensitive to case or not
IgnoreCase bool ` json:"ignorecase" `
// ValidatorRegexp extracts a subset of the player's answer, that will be checked.
ValidatorRegexp * string ` json:"validator_regexp" `
// Checksum is the expected hashed flag
Checksum [ ] byte ` json:"value" `
// ChoicesCost is the number of points lost to display choices.
ChoicesCost int64 ` json:"choices_cost" `
}
// GetFlagKeys returns a list of key's flags comming with the challenge.
func ( e Exercice ) GetFlagKeys ( ) ( [ ] FlagKey , error ) {
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 {
return nil , err
} else {
defer rows . Close ( )
var flags = make ( [ ] FlagKey , 0 )
for rows . Next ( ) {
var k FlagKey
k . IdExercice = e . Id
if err := rows . Scan ( & k . Id , & k . IdExercice , & k . Label , & k . Help , & k . IgnoreCase , & k . ValidatorRegexp , & k . Checksum , & k . ChoicesCost ) ; err != nil {
return nil , err
}
flags = append ( flags , k )
}
if err := rows . Err ( ) ; err != nil {
return nil , err
}
return flags , nil
}
}
// GetFlagKey returns a list of flags comming with the challenge.
func GetFlagKey ( id int64 ) ( k FlagKey , 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
}
// GetFlagKeyByLabel returns a flag matching the given label.
func ( e Exercice ) GetFlagKeyByLabel ( label string ) ( k FlagKey , err error ) {
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 )
return
}
// getHashedFlag calculates the expected checksum for the given raw_value.
func getHashedFlag ( raw_value [ ] byte ) [ blake2b . Size ] byte {
hash := blake2b . Sum512 ( raw_value )
return hash
}
2019-01-16 03:52:34 +00:00
func ExecValidatorRegexp ( vre string , val [ ] byte ) ( [ ] byte , error ) {
if re , err := regexp . Compile ( vre ) ; err != nil {
return val , err
} else if res := re . FindSubmatch ( val ) ; res == nil {
return val , errors . New ( "Expected flag doesn't pass through the validator_regexp" )
} else {
return bytes . Join ( res [ 1 : ] , [ ] byte ( "+" ) ) , nil
}
}
2019-01-02 20:51:09 +00:00
// AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database.
func ( e Exercice ) AddRawFlagKey ( name string , help string , ignorecase bool , validator_regexp * string , raw_value [ ] byte , choicescost int64 ) ( FlagKey , error ) {
if ignorecase {
raw_value = bytes . ToLower ( raw_value )
}
// Check that raw value passes through the regexp
if validator_regexp != nil {
2019-01-16 03:52:34 +00:00
var err error
if raw_value , err = ExecValidatorRegexp ( * validator_regexp , raw_value ) ; err != nil {
2019-01-02 20:51:09 +00:00
return FlagKey { } , err
}
}
hash := getHashedFlag ( raw_value )
return e . AddFlagKey ( name , help , ignorecase , validator_regexp , hash [ : ] , choicescost )
}
// AddFlagKey creates and fills a new struct Flag, from a hashed flag, and registers it into the database.
func ( e Exercice ) AddFlagKey ( name string , help string , ignorecase bool , validator_regexp * string , checksum [ ] byte , choicescost int64 ) ( FlagKey , error ) {
// Check the regexp compile
if validator_regexp != nil {
if _ , err := regexp . Compile ( * validator_regexp ) ; err != nil {
return FlagKey { } , err
}
}
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 {
return FlagKey { } , err
} else if kid , err := res . LastInsertId ( ) ; err != nil {
return FlagKey { } , err
} else {
return FlagKey { kid , e . Id , name , help , ignorecase , validator_regexp , checksum , choicescost } , nil
}
}
2019-01-21 12:35:09 +00:00
// SetChecksumFromValue .
func ( k FlagKey ) ComputeChecksum ( val [ ] byte ) ( cksum [ ] byte , err error ) {
if k . IgnoreCase {
val = bytes . ToLower ( val )
}
// Check that raw value passes through the regexp
if k . ValidatorRegexp != nil {
if val , err = ExecValidatorRegexp ( * k . ValidatorRegexp , val ) ; err != nil {
return
}
}
// Check that the value is not empty
if len ( val ) == 0 {
err = errors . New ( "Empty flag after applying filters" )
}
hash := getHashedFlag ( val )
return hash [ : ] , err
}
2019-01-02 20:51:09 +00:00
// Update applies modifications back to the database.
func ( k FlagKey ) Update ( ) ( int64 , error ) {
if k . ValidatorRegexp != nil {
if _ , err := regexp . Compile ( * k . ValidatorRegexp ) ; err != nil {
return 0 , err
}
}
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 {
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 FlagKey ) Delete ( ) ( int64 , error ) {
if _ , err := DBExec ( "DELETE FROM exercice_files_deps WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
2019-01-16 05:02:06 +00:00
} else if _ , err := DBExec ( "DELETE FROM exercice_mcq_okey_deps WHERE id_flag_dep = ?" , k . Id ) ; err != nil {
return 0 , err
} else if _ , err := DBExec ( "DELETE FROM exercice_flags_omcq_deps WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
} else if _ , err := DBExec ( "DELETE FROM exercice_flags_deps WHERE id_flag = ? OR id_flag_dep = ?" , k . Id , k . Id ) ; err != nil {
2019-01-02 20:51:09 +00:00
return 0 , err
} else if _ , err := DBExec ( "DELETE FROM flag_choices WHERE id_flag = ?" , k . Id ) ; err != nil {
return 0 , err
} 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
}
}
// AddDepend insert a new dependency to a given flag.
func ( k FlagKey ) AddDepend ( j Flag ) ( err error ) {
if d , ok := j . ( FlagKey ) ; ok {
_ , err = DBExec ( "INSERT INTO exercice_flags_deps (id_flag, id_flag_dep) VALUES (?, ?)" , k . Id , d . Id )
2019-01-16 05:02:06 +00:00
} else if d , ok := j . ( MCQ ) ; ok {
_ , err = DBExec ( "INSERT INTO exercice_flags_omcq_deps (id_flag, id_mcq_dep) VALUES (?, ?)" , k . Id , d . Id )
2019-01-02 20:51:09 +00:00
} else {
2019-01-16 05:02:06 +00:00
err = errors . New ( fmt . Sprintf ( "Dependancy type for key (%T) not implemented for this flag." , j ) )
2019-01-02 20:51:09 +00:00
}
return
}
// GetDepends retrieve the flag's dependency list.
func ( k FlagKey ) GetDepends ( ) ( [ ] Flag , error ) {
2019-01-16 05:02:06 +00:00
var deps = make ( [ ] Flag , 0 )
2019-01-02 20:51:09 +00:00
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 ( )
for rows . Next ( ) {
var d int64
if err := rows . Scan ( & d ) ; err != nil {
return nil , err
}
2019-01-20 00:28:46 +00:00
deps = append ( deps , FlagKey { Id : d , IdExercice : k . IdExercice } )
2019-01-02 20:51:09 +00:00
}
if err := rows . Err ( ) ; err != nil {
return nil , err
}
2019-01-16 05:02:06 +00:00
}
2019-01-02 20:51:09 +00:00
2019-01-16 05:02:06 +00:00
if rows , err := DBQuery ( "SELECT id_mcq_dep FROM exercice_flags_omcq_deps WHERE id_flag = ?" , k . Id ) ; err != nil {
return nil , err
} else {
defer rows . Close ( )
for rows . Next ( ) {
var d int64
if err := rows . Scan ( & d ) ; err != nil {
return nil , err
}
2019-01-20 00:28:46 +00:00
deps = append ( deps , MCQ { Id : d , IdExercice : k . IdExercice } )
2019-01-16 05:02:06 +00:00
}
if err := rows . Err ( ) ; err != nil {
return nil , err
}
2019-01-02 20:51:09 +00:00
}
2019-01-16 05:02:06 +00:00
return deps , nil
2019-01-02 20:51:09 +00:00
}
// Check if the given val is the expected one for this flag.
func ( k FlagKey ) Check ( v interface { } ) int {
var val [ ] byte
if va , ok := v . ( [ ] byte ) ; ! ok {
return - 1
} else {
val = va
}
2019-01-21 12:35:09 +00:00
hash , err := k . ComputeChecksum ( val )
if err != nil {
2019-01-02 20:51:09 +00:00
return 1
}
if len ( k . Checksum ) != len ( hash ) {
return 1
}
for i := range hash {
if k . Checksum [ i ] != hash [ i ] {
return 1
}
}
return 0
}
// FoundBy registers in the database that the given Team solved the flag.
func ( k FlagKey ) FoundBy ( t Team ) {
DBExec ( "INSERT INTO flag_found (id_flag, id_team, time) VALUES (?, ?, ?)" , k . Id , t . Id , time . Now ( ) )
}