package dtls
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/binary"
"math/big"
"time"
"github.com/pion/dtls/v2/pkg/crypto/elliptic"
"github.com/pion/dtls/v2/pkg/crypto/hash"
)
type ecdsaSignature struct {
R, S *big .Int
}
func valueKeyMessage(clientRandom , serverRandom , publicKey []byte , namedCurve elliptic .Curve ) []byte {
serverECDHParams := make ([]byte , 4 )
serverECDHParams [0 ] = 3
binary .BigEndian .PutUint16 (serverECDHParams [1 :], uint16 (namedCurve ))
serverECDHParams [3 ] = byte (len (publicKey ))
plaintext := []byte {}
plaintext = append (plaintext , clientRandom ...)
plaintext = append (plaintext , serverRandom ...)
plaintext = append (plaintext , serverECDHParams ...)
plaintext = append (plaintext , publicKey ...)
return plaintext
}
func generateKeySignature(clientRandom , serverRandom , publicKey []byte , namedCurve elliptic .Curve , privateKey crypto .PrivateKey , hashAlgorithm hash .Algorithm ) ([]byte , error ) {
msg := valueKeyMessage (clientRandom , serverRandom , publicKey , namedCurve )
switch p := privateKey .(type ) {
case ed25519 .PrivateKey :
return p .Sign (rand .Reader , msg , crypto .Hash (0 ))
case *ecdsa .PrivateKey :
hashed := hashAlgorithm .Digest (msg )
return p .Sign (rand .Reader , hashed , hashAlgorithm .CryptoHash ())
case *rsa .PrivateKey :
hashed := hashAlgorithm .Digest (msg )
return p .Sign (rand .Reader , hashed , hashAlgorithm .CryptoHash ())
}
return nil , errKeySignatureGenerateUnimplemented
}
func verifyKeySignature(message , remoteKeySignature []byte , hashAlgorithm hash .Algorithm , rawCertificates [][]byte ) error {
if len (rawCertificates ) == 0 {
return errLengthMismatch
}
certificate , err := x509 .ParseCertificate (rawCertificates [0 ])
if err != nil {
return err
}
switch p := certificate .PublicKey .(type ) {
case ed25519 .PublicKey :
if ok := ed25519 .Verify (p , message , remoteKeySignature ); !ok {
return errKeySignatureMismatch
}
return nil
case *ecdsa .PublicKey :
ecdsaSig := &ecdsaSignature {}
if _ , err := asn1 .Unmarshal (remoteKeySignature , ecdsaSig ); err != nil {
return err
}
if ecdsaSig .R .Sign () <= 0 || ecdsaSig .S .Sign () <= 0 {
return errInvalidECDSASignature
}
hashed := hashAlgorithm .Digest (message )
if !ecdsa .Verify (p , hashed , ecdsaSig .R , ecdsaSig .S ) {
return errKeySignatureMismatch
}
return nil
case *rsa .PublicKey :
switch certificate .SignatureAlgorithm {
case x509 .SHA1WithRSA , x509 .SHA256WithRSA , x509 .SHA384WithRSA , x509 .SHA512WithRSA :
hashed := hashAlgorithm .Digest (message )
return rsa .VerifyPKCS1v15 (p , hashAlgorithm .CryptoHash (), hashed , remoteKeySignature )
default :
return errKeySignatureVerifyUnimplemented
}
}
return errKeySignatureVerifyUnimplemented
}
func generateCertificateVerify(handshakeBodies []byte , privateKey crypto .PrivateKey , hashAlgorithm hash .Algorithm ) ([]byte , error ) {
if p , ok := privateKey .(ed25519 .PrivateKey ); ok {
return p .Sign (rand .Reader , handshakeBodies , crypto .Hash (0 ))
}
h := sha256 .New ()
if _ , err := h .Write (handshakeBodies ); err != nil {
return nil , err
}
hashed := h .Sum (nil )
switch p := privateKey .(type ) {
case *ecdsa .PrivateKey :
return p .Sign (rand .Reader , hashed , hashAlgorithm .CryptoHash ())
case *rsa .PrivateKey :
return p .Sign (rand .Reader , hashed , hashAlgorithm .CryptoHash ())
}
return nil , errInvalidSignatureAlgorithm
}
func verifyCertificateVerify(handshakeBodies []byte , hashAlgorithm hash .Algorithm , remoteKeySignature []byte , rawCertificates [][]byte ) error {
if len (rawCertificates ) == 0 {
return errLengthMismatch
}
certificate , err := x509 .ParseCertificate (rawCertificates [0 ])
if err != nil {
return err
}
switch p := certificate .PublicKey .(type ) {
case ed25519 .PublicKey :
if ok := ed25519 .Verify (p , handshakeBodies , remoteKeySignature ); !ok {
return errKeySignatureMismatch
}
return nil
case *ecdsa .PublicKey :
ecdsaSig := &ecdsaSignature {}
if _ , err := asn1 .Unmarshal (remoteKeySignature , ecdsaSig ); err != nil {
return err
}
if ecdsaSig .R .Sign () <= 0 || ecdsaSig .S .Sign () <= 0 {
return errInvalidECDSASignature
}
hash := hashAlgorithm .Digest (handshakeBodies )
if !ecdsa .Verify (p , hash , ecdsaSig .R , ecdsaSig .S ) {
return errKeySignatureMismatch
}
return nil
case *rsa .PublicKey :
switch certificate .SignatureAlgorithm {
case x509 .SHA1WithRSA , x509 .SHA256WithRSA , x509 .SHA384WithRSA , x509 .SHA512WithRSA :
hash := hashAlgorithm .Digest (handshakeBodies )
return rsa .VerifyPKCS1v15 (p , hashAlgorithm .CryptoHash (), hash , remoteKeySignature )
default :
return errKeySignatureVerifyUnimplemented
}
}
return errKeySignatureVerifyUnimplemented
}
func loadCerts(rawCertificates [][]byte ) ([]*x509 .Certificate , error ) {
if len (rawCertificates ) == 0 {
return nil , errLengthMismatch
}
certs := make ([]*x509 .Certificate , 0 , len (rawCertificates ))
for _ , rawCert := range rawCertificates {
cert , err := x509 .ParseCertificate (rawCert )
if err != nil {
return nil , err
}
certs = append (certs , cert )
}
return certs , nil
}
func verifyClientCert(rawCertificates [][]byte , roots *x509 .CertPool ) (chains [][]*x509 .Certificate , err error ) {
certificate , err := loadCerts (rawCertificates )
if err != nil {
return nil , err
}
intermediateCAPool := x509 .NewCertPool ()
for _ , cert := range certificate [1 :] {
intermediateCAPool .AddCert (cert )
}
opts := x509 .VerifyOptions {
Roots : roots ,
CurrentTime : time .Now (),
Intermediates : intermediateCAPool ,
KeyUsages : []x509 .ExtKeyUsage {x509 .ExtKeyUsageClientAuth },
}
return certificate [0 ].Verify (opts )
}
func verifyServerCert(rawCertificates [][]byte , roots *x509 .CertPool , serverName string ) (chains [][]*x509 .Certificate , err error ) {
certificate , err := loadCerts (rawCertificates )
if err != nil {
return nil , err
}
intermediateCAPool := x509 .NewCertPool ()
for _ , cert := range certificate [1 :] {
intermediateCAPool .AddCert (cert )
}
opts := x509 .VerifyOptions {
Roots : roots ,
CurrentTime : time .Now (),
DNSName : serverName ,
Intermediates : intermediateCAPool ,
}
return certificate [0 ].Verify (opts )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux 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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds .