func (ch *authHSChan) validatePacket(pkt *packet.AuthHSPacket) error { nSet := 0 if msg := pkt.GetProof(); msg != nil { nSet++ if pkDER := msg.GetPublicKey(); pkDER == nil { return fmt.Errorf("missing public_key") } if sig := msg.GetSignature(); sig == nil { return fmt.Errorf("missing signature") } } if msg := pkt.GetResult(); msg != nil { nSet++ } if nSet != 1 { return fmt.Errorf("has %d fields set", nSet) } return nil }
func (ch *authHSChan) onPacketClient(authPkt *packet.AuthHSPacket) error { log := ch.conn.endpoint.log resultMsg := authPkt.GetResult() if resultMsg == nil { return fmt.Errorf("missing result") } if !resultMsg.GetAccepted() { ch.conn.endpoint.onRemoteReject(ch.conn.hostname) return fmt.Errorf("client: auth to '%s' rejected", ch.conn.hostname) } isKnown := resultMsg.GetIsKnownContact() log.Printf("client: auth to server '%s' accepted isKnown: %v", ch.conn.hostname, isKnown) ch.conn.getControlChan().isAuthenticated = true ch.conn.getControlChan().isKnownToPeer = isKnown ch.conn.authTimer.Stop() // Stop the f**k(). ch.conn.setEstablished() // XXX: Send a channel close? This is something the server ought to be // doing, so don't bother for now. This code will not use this channel // past this point, apart from processing the server's close. if isKnown { ch.conn.endpoint.onConnectionEstablished(ch.conn) return nil } // The peer doesn't immediately recognize us. If this is expected, // dispatch a ContactRequest, otherwise, we got removed. requestData := ch.conn.endpoint.requestData(ch.conn.hostname) if requestData == nil { ch.conn.endpoint.onRemoteReject(ch.conn.hostname) return fmt.Errorf("client: remote peer remove us from contacts") } return newClientContactReqChan(ch.conn, requestData) }
func (ch *authHSChan) onPacketServer(authPkt *packet.AuthHSPacket) error { log := ch.conn.endpoint.log proofMsg := authPkt.GetProof() if proofMsg == nil { return fmt.Errorf("missing proof") } // Decode and validate the public key. pk, rest, err := pkcs1.DecodePublicKeyDER(proofMsg.GetPublicKey()) if err != nil { return err } else if rest != nil && len(rest) > 0 { return fmt.Errorf("trailing garbage present after public key") } else if pk.N.BitLen() != authHiddenServicePublicKeyBits { return fmt.Errorf("invalid public modulus size: %d", pk.N.BitLen()) } // Calculate the client hostname. clientHostname, err := pkcs1.OnionAddr(pk) if err != nil { return err } sigOk, isKnown := false, false // Note: The reference implementation checks against the hostname // blacklist for rejected peers, and early rejects clients durring // the authentication phase by closing the connection. // // This is a bit rude. Check the blacklist and send a rejected // response, then close the connection (Done by virtue of returning // an error). if !ch.conn.endpoint.isBlacklisted(clientHostname) { // Calculate the proof. proof := ch.calculateProof(clientHostname, ch.conn.endpoint.hostname) // Verify proof. // // The spec neglects to mention PKCS #1 v1.5/SHA256. Also, not // SHA256 suming proof is deliberate because the ricochet code // doesn't bother hashing the proof. sig := proofMsg.GetSignature() err = rsa.VerifyPKCS1v15(pk, crypto.SHA256, proof, sig) if err == nil { sigOk = true ch.conn.getControlChan().isAuthenticated = true ch.conn.getControlChan().isKnownToPeer = true ch.conn.hostname = clientHostname isKnown = ch.conn.endpoint.isKnown(clientHostname) } } else { err = fmt.Errorf("auth from blacklisted peer: '%v'", clientHostname) } log.Printf("server: auth from client '%s' accepted: %v", clientHostname, sigOk) // Send the result to the client. if wrErr := ch.sendResult(sigOk, isKnown); wrErr != nil { return wrErr } if sigOk { ch.conn.setEstablished() if isKnown { ch.conn.authTimer.Stop() ch.conn.endpoint.onConnectionEstablished(ch.conn) } else { // Give them another interval to request contact. ch.conn.authTimer.Reset(authenticationTimeout) } } // Close the authentication channel. if wrErr := ch.conn.sendChanClose(ch.chanID); wrErr != nil { return wrErr } return err }