package main
import ("crypto/hmac";"crypto/sha256";"encoding/base64";"encoding/binary";"encoding/hex";"errors")

const MaxMessage=65536
var ErrEncoding=errors.New("invalid or noncanonical encoding")
func EncodeExact(name string,data []byte)(string,error){
 if len(data)>MaxMessage{return "",ErrEncoding}
 switch name {
 case "hex":return hex.EncodeToString(data),nil
 case "base64":return base64.StdEncoding.EncodeToString(data),nil
 default:return "",ErrEncoding
 }
}
func DecodeExact(name,raw string)([]byte,error){
 if len(raw)>2*MaxMessage{return nil,ErrEncoding}
 var data []byte;var err error
 switch name {
 case "hex":data,err=hex.DecodeString(raw)
 case "base64":data,err=base64.StdEncoding.Strict().DecodeString(raw)
 default:return nil,ErrEncoding
 }
 if err!=nil||len(data)>MaxMessage{return nil,ErrEncoding}
 canonical,err:=EncodeExact(name,data)
 if err!=nil||canonical!=raw{return nil,ErrEncoding}
 return data,nil
}

// This low-level wrapper permits the published vectors' fixed test keys.
// The application record API separately requires a 32-byte random key.
func HMAC256(key,message []byte)[]byte{
 mac:=hmac.New(sha256.New,key)
 _,_=mac.Write(message)
 return mac.Sum(nil)
}
func VerifyHMAC256(key,message,tag []byte)bool{
 if len(tag)!=sha256.Size{return false}
 return hmac.Equal(HMAC256(key,message),tag)
}

var ErrRecord=errors.New("invalid authenticated record")
type Record struct{
 Version int `json:"version"`
 Context string `json:"context"`
 Message string `json:"message"`
 Tag string `json:"tag"`
}
func validContext(label string)bool{
 if len(label)<1||len(label)>64{return false}
 for _,b:=range []byte(label){if !(b>='a'&&b<='z'||b>='0'&&b<='9'||b=='.'||b=='_'||b=='-'){return false}}
 return true
}
func recordFrame(label string,message []byte)[]byte{
 result:=[]byte("coderigor.record.v1\x00")
 result=binary.BigEndian.AppendUint64(result,uint64(len(label)))
 result=append(result,[]byte(label)...)
 result=binary.BigEndian.AppendUint64(result,uint64(len(message)))
 return append(result,message...)
}
func SignRecord(key []byte,label string,message []byte)(Record,error){
 if len(key)!=32||!validContext(label)||len(message)>MaxMessage{return Record{},ErrRecord}
 raw,err:=EncodeExact("base64",message);if err!=nil{return Record{},ErrRecord}
 return Record{Version:1,Context:label,Message:raw,Tag:hex.EncodeToString(HMAC256(key,recordFrame(label,message)))},nil
}
func VerifyRecord(key []byte,expectedContext string,record Record)([]byte,error){
    panic("implement the stated contract")
}
