package tls
import (
"bytes"
"crypto"
"crypto/hmac"
"crypto/rsa"
"errors"
"hash"
"io"
"sync/atomic"
"time"
)
const maxClientPSKIdentities = 5
type serverHandshakeStateTLS13 struct {
c *Conn
clientHello *clientHelloMsg
hello *serverHelloMsg
sentDummyCCS bool
usingPSK bool
suite *cipherSuiteTLS13
cert *Certificate
sigAlg SignatureScheme
earlySecret []byte
sharedKey []byte
handshakeSecret []byte
masterSecret []byte
trafficSecret []byte
transcript hash .Hash
clientFinished []byte
}
func (hs *serverHandshakeStateTLS13 ) handshake () error {
c := hs .c
if err := hs .processClientHello (); err != nil {
return err
}
if err := hs .checkForResumption (); err != nil {
return err
}
if err := hs .pickCertificate (); err != nil {
return err
}
c .buffering = true
if err := hs .sendServerParameters (); err != nil {
return err
}
if err := hs .sendServerCertificate (); err != nil {
return err
}
if err := hs .sendServerFinished (); err != nil {
return err
}
if _ , err := c .flush (); err != nil {
return err
}
if err := hs .readClientCertificate (); err != nil {
return err
}
if err := hs .readClientFinished (); err != nil {
return err
}
atomic .StoreUint32 (&c .handshakeStatus , 1 )
return nil
}
func (hs *serverHandshakeStateTLS13 ) processClientHello () error {
c := hs .c
hs .hello = new (serverHelloMsg )
hs .hello .vers = VersionTLS12
hs .hello .supportedVersion = c .vers
if len (hs .clientHello .supportedVersions ) == 0 {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client used the legacy version field to negotiate TLS 1.3" )
}
for _ , id := range hs .clientHello .cipherSuites {
if id == TLS_FALLBACK_SCSV {
if c .vers < c .config .maxSupportedVersion () {
c .sendAlert (alertInappropriateFallback )
return errors .New ("tls: client using inappropriate protocol fallback" )
}
break
}
}
if len (hs .clientHello .compressionMethods ) != 1 ||
hs .clientHello .compressionMethods [0 ] != compressionNone {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: TLS 1.3 client supports illegal compression methods" )
}
hs .hello .random = make ([]byte , 32 )
if _ , err := io .ReadFull (c .config .rand (), hs .hello .random ); err != nil {
c .sendAlert (alertInternalError )
return err
}
if len (hs .clientHello .secureRenegotiation ) != 0 {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: initial handshake had non-empty renegotiation extension" )
}
if hs .clientHello .earlyData {
c .sendAlert (alertUnsupportedExtension )
return errors .New ("tls: client sent unexpected early data" )
}
hs .hello .sessionId = hs .clientHello .sessionId
hs .hello .compressionMethod = compressionNone
var preferenceList , supportedList []uint16
if c .config .PreferServerCipherSuites {
preferenceList = defaultCipherSuitesTLS13 ()
supportedList = hs .clientHello .cipherSuites
if !aesgcmPreferred (hs .clientHello .cipherSuites ) {
preferenceList = deprioritizeAES (preferenceList )
}
} else {
preferenceList = hs .clientHello .cipherSuites
supportedList = defaultCipherSuitesTLS13 ()
if !hasAESGCMHardwareSupport {
preferenceList = deprioritizeAES (preferenceList )
}
}
for _ , suiteID := range preferenceList {
hs .suite = mutualCipherSuiteTLS13 (supportedList , suiteID )
if hs .suite != nil {
break
}
}
if hs .suite == nil {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: no cipher suite supported by both client and server" )
}
c .cipherSuite = hs .suite .id
hs .hello .cipherSuite = hs .suite .id
hs .transcript = hs .suite .hash .New ()
var selectedGroup CurveID
var clientKeyShare *keyShare
GroupSelection :
for _ , preferredGroup := range c .config .curvePreferences () {
for _ , ks := range hs .clientHello .keyShares {
if ks .group == preferredGroup {
selectedGroup = ks .group
clientKeyShare = &ks
break GroupSelection
}
}
if selectedGroup != 0 {
continue
}
for _ , group := range hs .clientHello .supportedCurves {
if group == preferredGroup {
selectedGroup = group
break
}
}
}
if selectedGroup == 0 {
c .sendAlert (alertHandshakeFailure )
return errors .New ("tls: no ECDHE curve supported by both client and server" )
}
if clientKeyShare == nil {
if err := hs .doHelloRetryRequest (selectedGroup ); err != nil {
return err
}
clientKeyShare = &hs .clientHello .keyShares [0 ]
}
if _ , ok := curveForCurveID (selectedGroup ); selectedGroup != X25519 && !ok {
c .sendAlert (alertInternalError )
return errors .New ("tls: CurvePreferences includes unsupported curve" )
}
params , err := generateECDHEParameters (c .config .rand (), selectedGroup )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
hs .hello .serverShare = keyShare {group : selectedGroup , data : params .PublicKey ()}
hs .sharedKey = params .SharedKey (clientKeyShare .data )
if hs .sharedKey == nil {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: invalid client key share" )
}
c .serverName = hs .clientHello .serverName
return nil
}
func (hs *serverHandshakeStateTLS13 ) checkForResumption () error {
c := hs .c
if c .config .SessionTicketsDisabled {
return nil
}
modeOK := false
for _ , mode := range hs .clientHello .pskModes {
if mode == pskModeDHE {
modeOK = true
break
}
}
if !modeOK {
return nil
}
if len (hs .clientHello .pskIdentities ) != len (hs .clientHello .pskBinders ) {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: invalid or missing PSK binders" )
}
if len (hs .clientHello .pskIdentities ) == 0 {
return nil
}
for i , identity := range hs .clientHello .pskIdentities {
if i >= maxClientPSKIdentities {
break
}
plaintext , _ := c .decryptTicket (identity .label )
if plaintext == nil {
continue
}
sessionState := new (sessionStateTLS13 )
if ok := sessionState .unmarshal (plaintext ); !ok {
continue
}
createdAt := time .Unix (int64 (sessionState .createdAt ), 0 )
if c .config .time ().Sub (createdAt ) > maxSessionTicketLifetime {
continue
}
pskSuite := cipherSuiteTLS13ByID (sessionState .cipherSuite )
if pskSuite == nil || pskSuite .hash != hs .suite .hash {
continue
}
sessionHasClientCerts := len (sessionState .certificate .Certificate ) != 0
needClientCerts := requiresClientCert (c .config .ClientAuth )
if needClientCerts && !sessionHasClientCerts {
continue
}
if sessionHasClientCerts && c .config .ClientAuth == NoClientCert {
continue
}
psk := hs .suite .expandLabel (sessionState .resumptionSecret , "resumption" ,
nil , hs .suite .hash .Size ())
hs .earlySecret = hs .suite .extract (psk , nil )
binderKey := hs .suite .deriveSecret (hs .earlySecret , resumptionBinderLabel , nil )
transcript := cloneHash (hs .transcript , hs .suite .hash )
if transcript == nil {
c .sendAlert (alertInternalError )
return errors .New ("tls: internal error: failed to clone hash" )
}
transcript .Write (hs .clientHello .marshalWithoutBinders ())
pskBinder := hs .suite .finishedHash (binderKey , transcript )
if !hmac .Equal (hs .clientHello .pskBinders [i ], pskBinder ) {
c .sendAlert (alertDecryptError )
return errors .New ("tls: invalid PSK binder" )
}
c .didResume = true
if err := c .processCertsFromClient (sessionState .certificate ); err != nil {
return err
}
hs .hello .selectedIdentityPresent = true
hs .hello .selectedIdentity = uint16 (i )
hs .usingPSK = true
return nil
}
return nil
}
func cloneHash (in hash .Hash , h crypto .Hash ) hash .Hash {
type binaryMarshaler interface {
MarshalBinary () (data []byte , err error )
UnmarshalBinary (data []byte ) error
}
marshaler , ok := in .(binaryMarshaler )
if !ok {
return nil
}
state , err := marshaler .MarshalBinary ()
if err != nil {
return nil
}
out := h .New ()
unmarshaler , ok := out .(binaryMarshaler )
if !ok {
return nil
}
if err := unmarshaler .UnmarshalBinary (state ); err != nil {
return nil
}
return out
}
func (hs *serverHandshakeStateTLS13 ) pickCertificate () error {
c := hs .c
if hs .usingPSK {
return nil
}
if len (hs .clientHello .supportedSignatureAlgorithms ) == 0 {
return c .sendAlert (alertMissingExtension )
}
certificate , err := c .config .getCertificate (clientHelloInfo (c , hs .clientHello ))
if err != nil {
if err == errNoCertificates {
c .sendAlert (alertUnrecognizedName )
} else {
c .sendAlert (alertInternalError )
}
return err
}
hs .sigAlg , err = selectSignatureScheme (c .vers , certificate , hs .clientHello .supportedSignatureAlgorithms )
if err != nil {
c .sendAlert (alertHandshakeFailure )
return err
}
hs .cert = certificate
return nil
}
func (hs *serverHandshakeStateTLS13 ) sendDummyChangeCipherSpec () error {
if hs .sentDummyCCS {
return nil
}
hs .sentDummyCCS = true
_ , err := hs .c .writeRecord (recordTypeChangeCipherSpec , []byte {1 })
return err
}
func (hs *serverHandshakeStateTLS13 ) doHelloRetryRequest (selectedGroup CurveID ) error {
c := hs .c
hs .transcript .Write (hs .clientHello .marshal ())
chHash := hs .transcript .Sum (nil )
hs .transcript .Reset ()
hs .transcript .Write ([]byte {typeMessageHash , 0 , 0 , uint8 (len (chHash ))})
hs .transcript .Write (chHash )
helloRetryRequest := &serverHelloMsg {
vers : hs .hello .vers ,
random : helloRetryRequestRandom ,
sessionId : hs .hello .sessionId ,
cipherSuite : hs .hello .cipherSuite ,
compressionMethod : hs .hello .compressionMethod ,
supportedVersion : hs .hello .supportedVersion ,
selectedGroup : selectedGroup ,
}
hs .transcript .Write (helloRetryRequest .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , helloRetryRequest .marshal ()); err != nil {
return err
}
if err := hs .sendDummyChangeCipherSpec (); err != nil {
return err
}
msg , err := c .readHandshake ()
if err != nil {
return err
}
clientHello , ok := msg .(*clientHelloMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (clientHello , msg )
}
if len (clientHello .keyShares ) != 1 || clientHello .keyShares [0 ].group != selectedGroup {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client sent invalid key share in second ClientHello" )
}
if clientHello .earlyData {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client indicated early data in second ClientHello" )
}
if illegalClientHelloChange (clientHello , hs .clientHello ) {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client illegally modified second ClientHello" )
}
hs .clientHello = clientHello
return nil
}
func illegalClientHelloChange (ch , ch1 *clientHelloMsg ) bool {
if len (ch .supportedVersions ) != len (ch1 .supportedVersions ) ||
len (ch .cipherSuites ) != len (ch1 .cipherSuites ) ||
len (ch .supportedCurves ) != len (ch1 .supportedCurves ) ||
len (ch .supportedSignatureAlgorithms ) != len (ch1 .supportedSignatureAlgorithms ) ||
len (ch .supportedSignatureAlgorithmsCert ) != len (ch1 .supportedSignatureAlgorithmsCert ) ||
len (ch .alpnProtocols ) != len (ch1 .alpnProtocols ) {
return true
}
for i := range ch .supportedVersions {
if ch .supportedVersions [i ] != ch1 .supportedVersions [i ] {
return true
}
}
for i := range ch .cipherSuites {
if ch .cipherSuites [i ] != ch1 .cipherSuites [i ] {
return true
}
}
for i := range ch .supportedCurves {
if ch .supportedCurves [i ] != ch1 .supportedCurves [i ] {
return true
}
}
for i := range ch .supportedSignatureAlgorithms {
if ch .supportedSignatureAlgorithms [i ] != ch1 .supportedSignatureAlgorithms [i ] {
return true
}
}
for i := range ch .supportedSignatureAlgorithmsCert {
if ch .supportedSignatureAlgorithmsCert [i ] != ch1 .supportedSignatureAlgorithmsCert [i ] {
return true
}
}
for i := range ch .alpnProtocols {
if ch .alpnProtocols [i ] != ch1 .alpnProtocols [i ] {
return true
}
}
return ch .vers != ch1 .vers ||
!bytes .Equal (ch .random , ch1 .random ) ||
!bytes .Equal (ch .sessionId , ch1 .sessionId ) ||
!bytes .Equal (ch .compressionMethods , ch1 .compressionMethods ) ||
ch .serverName != ch1 .serverName ||
ch .ocspStapling != ch1 .ocspStapling ||
!bytes .Equal (ch .supportedPoints , ch1 .supportedPoints ) ||
ch .ticketSupported != ch1 .ticketSupported ||
!bytes .Equal (ch .sessionTicket , ch1 .sessionTicket ) ||
ch .secureRenegotiationSupported != ch1 .secureRenegotiationSupported ||
!bytes .Equal (ch .secureRenegotiation , ch1 .secureRenegotiation ) ||
ch .scts != ch1 .scts ||
!bytes .Equal (ch .cookie , ch1 .cookie ) ||
!bytes .Equal (ch .pskModes , ch1 .pskModes )
}
func (hs *serverHandshakeStateTLS13 ) sendServerParameters () error {
c := hs .c
hs .transcript .Write (hs .clientHello .marshal ())
hs .transcript .Write (hs .hello .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , hs .hello .marshal ()); err != nil {
return err
}
if err := hs .sendDummyChangeCipherSpec (); err != nil {
return err
}
earlySecret := hs .earlySecret
if earlySecret == nil {
earlySecret = hs .suite .extract (nil , nil )
}
hs .handshakeSecret = hs .suite .extract (hs .sharedKey ,
hs .suite .deriveSecret (earlySecret , "derived" , nil ))
clientSecret := hs .suite .deriveSecret (hs .handshakeSecret ,
clientHandshakeTrafficLabel , hs .transcript )
c .in .setTrafficSecret (hs .suite , clientSecret )
serverSecret := hs .suite .deriveSecret (hs .handshakeSecret ,
serverHandshakeTrafficLabel , hs .transcript )
c .out .setTrafficSecret (hs .suite , serverSecret )
err := c .config .writeKeyLog (keyLogLabelClientHandshake , hs .clientHello .random , clientSecret )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
err = c .config .writeKeyLog (keyLogLabelServerHandshake , hs .clientHello .random , serverSecret )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
encryptedExtensions := new (encryptedExtensionsMsg )
if len (hs .clientHello .alpnProtocols ) > 0 {
if selectedProto := mutualProtocol (hs .clientHello .alpnProtocols , c .config .NextProtos ); selectedProto != "" {
encryptedExtensions .alpnProtocol = selectedProto
c .clientProtocol = selectedProto
}
}
hs .transcript .Write (encryptedExtensions .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , encryptedExtensions .marshal ()); err != nil {
return err
}
return nil
}
func (hs *serverHandshakeStateTLS13 ) requestClientCert () bool {
return hs .c .config .ClientAuth >= RequestClientCert && !hs .usingPSK
}
func (hs *serverHandshakeStateTLS13 ) sendServerCertificate () error {
c := hs .c
if hs .usingPSK {
return nil
}
if hs .requestClientCert () {
certReq := new (certificateRequestMsgTLS13 )
certReq .ocspStapling = true
certReq .scts = true
certReq .supportedSignatureAlgorithms = supportedSignatureAlgorithms
if c .config .ClientCAs != nil {
certReq .certificateAuthorities = c .config .ClientCAs .Subjects ()
}
hs .transcript .Write (certReq .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , certReq .marshal ()); err != nil {
return err
}
}
certMsg := new (certificateMsgTLS13 )
certMsg .certificate = *hs .cert
certMsg .scts = hs .clientHello .scts && len (hs .cert .SignedCertificateTimestamps ) > 0
certMsg .ocspStapling = hs .clientHello .ocspStapling && len (hs .cert .OCSPStaple ) > 0
hs .transcript .Write (certMsg .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , certMsg .marshal ()); err != nil {
return err
}
certVerifyMsg := new (certificateVerifyMsg )
certVerifyMsg .hasSignatureAlgorithm = true
certVerifyMsg .signatureAlgorithm = hs .sigAlg
sigType , sigHash , err := typeAndHashFromSignatureScheme (hs .sigAlg )
if err != nil {
return c .sendAlert (alertInternalError )
}
signed := signedMessage (sigHash , serverSignatureContext , hs .transcript )
signOpts := crypto .SignerOpts (sigHash )
if sigType == signatureRSAPSS {
signOpts = &rsa .PSSOptions {SaltLength : rsa .PSSSaltLengthEqualsHash , Hash : sigHash }
}
sig , err := hs .cert .PrivateKey .(crypto .Signer ).Sign (c .config .rand (), signed , signOpts )
if err != nil {
public := hs .cert .PrivateKey .(crypto .Signer ).Public ()
if rsaKey , ok := public .(*rsa .PublicKey ); ok && sigType == signatureRSAPSS &&
rsaKey .N .BitLen ()/8 < sigHash .Size ()*2 +2 {
c .sendAlert (alertHandshakeFailure )
} else {
c .sendAlert (alertInternalError )
}
return errors .New ("tls: failed to sign handshake: " + err .Error())
}
certVerifyMsg .signature = sig
hs .transcript .Write (certVerifyMsg .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , certVerifyMsg .marshal ()); err != nil {
return err
}
return nil
}
func (hs *serverHandshakeStateTLS13 ) sendServerFinished () error {
c := hs .c
finished := &finishedMsg {
verifyData : hs .suite .finishedHash (c .out .trafficSecret , hs .transcript ),
}
hs .transcript .Write (finished .marshal ())
if _ , err := c .writeRecord (recordTypeHandshake , finished .marshal ()); err != nil {
return err
}
hs .masterSecret = hs .suite .extract (nil ,
hs .suite .deriveSecret (hs .handshakeSecret , "derived" , nil ))
hs .trafficSecret = hs .suite .deriveSecret (hs .masterSecret ,
clientApplicationTrafficLabel , hs .transcript )
serverSecret := hs .suite .deriveSecret (hs .masterSecret ,
serverApplicationTrafficLabel , hs .transcript )
c .out .setTrafficSecret (hs .suite , serverSecret )
err := c .config .writeKeyLog (keyLogLabelClientTraffic , hs .clientHello .random , hs .trafficSecret )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
err = c .config .writeKeyLog (keyLogLabelServerTraffic , hs .clientHello .random , serverSecret )
if err != nil {
c .sendAlert (alertInternalError )
return err
}
c .ekm = hs .suite .exportKeyingMaterial (hs .masterSecret , hs .transcript )
if !hs .requestClientCert () {
if err := hs .sendSessionTickets (); err != nil {
return err
}
}
return nil
}
func (hs *serverHandshakeStateTLS13 ) shouldSendSessionTickets () bool {
if hs .c .config .SessionTicketsDisabled {
return false
}
for _ , pskMode := range hs .clientHello .pskModes {
if pskMode == pskModeDHE {
return true
}
}
return false
}
func (hs *serverHandshakeStateTLS13 ) sendSessionTickets () error {
c := hs .c
hs .clientFinished = hs .suite .finishedHash (c .in .trafficSecret , hs .transcript )
finishedMsg := &finishedMsg {
verifyData : hs .clientFinished ,
}
hs .transcript .Write (finishedMsg .marshal ())
if !hs .shouldSendSessionTickets () {
return nil
}
resumptionSecret := hs .suite .deriveSecret (hs .masterSecret ,
resumptionLabel , hs .transcript )
m := new (newSessionTicketMsgTLS13 )
var certsFromClient [][]byte
for _ , cert := range c .peerCertificates {
certsFromClient = append (certsFromClient , cert .Raw )
}
state := sessionStateTLS13 {
cipherSuite : hs .suite .id ,
createdAt : uint64 (c .config .time ().Unix ()),
resumptionSecret : resumptionSecret ,
certificate : Certificate {
Certificate : certsFromClient ,
OCSPStaple : c .ocspResponse ,
SignedCertificateTimestamps : c .scts ,
},
}
var err error
m .label , err = c .encryptTicket (state .marshal ())
if err != nil {
return err
}
m .lifetime = uint32 (maxSessionTicketLifetime / time .Second )
if _ , err := c .writeRecord (recordTypeHandshake , m .marshal ()); err != nil {
return err
}
return nil
}
func (hs *serverHandshakeStateTLS13 ) readClientCertificate () error {
c := hs .c
if !hs .requestClientCert () {
if c .config .VerifyConnection != nil {
if err := c .config .VerifyConnection (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
return nil
}
msg , err := c .readHandshake ()
if err != nil {
return err
}
certMsg , ok := msg .(*certificateMsgTLS13 )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certMsg , msg )
}
hs .transcript .Write (certMsg .marshal ())
if err := c .processCertsFromClient (certMsg .certificate ); err != nil {
return err
}
if c .config .VerifyConnection != nil {
if err := c .config .VerifyConnection (c .connectionStateLocked ()); err != nil {
c .sendAlert (alertBadCertificate )
return err
}
}
if len (certMsg .certificate .Certificate ) != 0 {
msg , err = c .readHandshake ()
if err != nil {
return err
}
certVerify , ok := msg .(*certificateVerifyMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (certVerify , msg )
}
if !isSupportedSignatureAlgorithm (certVerify .signatureAlgorithm , supportedSignatureAlgorithms ) {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client certificate used with invalid signature algorithm" )
}
sigType , sigHash , err := typeAndHashFromSignatureScheme (certVerify .signatureAlgorithm )
if err != nil {
return c .sendAlert (alertInternalError )
}
if sigType == signaturePKCS1v15 || sigHash == crypto .SHA1 {
c .sendAlert (alertIllegalParameter )
return errors .New ("tls: client certificate used with invalid signature algorithm" )
}
signed := signedMessage (sigHash , clientSignatureContext , hs .transcript )
if err := verifyHandshakeSignature (sigType , c .peerCertificates [0 ].PublicKey ,
sigHash , signed , certVerify .signature ); err != nil {
c .sendAlert (alertDecryptError )
return errors .New ("tls: invalid signature by the client certificate: " + err .Error())
}
hs .transcript .Write (certVerify .marshal ())
}
if err := hs .sendSessionTickets (); err != nil {
return err
}
return nil
}
func (hs *serverHandshakeStateTLS13 ) readClientFinished () error {
c := hs .c
msg , err := c .readHandshake ()
if err != nil {
return err
}
finished , ok := msg .(*finishedMsg )
if !ok {
c .sendAlert (alertUnexpectedMessage )
return unexpectedMessageError (finished , msg )
}
if !hmac .Equal (hs .clientFinished , finished .verifyData ) {
c .sendAlert (alertDecryptError )
return errors .New ("tls: invalid client finished hash" )
}
c .in .setTrafficSecret (hs .suite , hs .trafficSecret )
return nil
}
The pages are generated with Golds v0.3.6 . (GOOS=darwin GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .