///|
pub struct CertificateIdentity {
provider : @crypto.Provider
private_key : @crypto.PrivateKey
certificate : @crypto.Certificate
certificate_der : Bytes
key_type : CertificateKeyType
}
///|
pub fn CertificateIdentity::generate(
common_name? : String = "moonbit-rtc",
key_type? : CertificateKeyType = EcdsaCertificate,
not_before~ : @transport.WallTime,
not_after~ : @transport.WallTime,
) -> CertificateIdentity raise DtlsError {
if not_after <= not_before {
raise HandshakeFailed("certificate validity interval is empty")
}
let provider = crypto_provider()
let key_algorithm = match key_type {
EcdsaCertificate => @crypto.KeyAlgorithm::P256
RsaCertificate => Rsa2048
}
let private_key = crypto_operation(() => {
provider.generate_private_key(key_algorithm)
})
let certificate = crypto_operation(() => {
provider.self_signed_certificate(
private_key, common_name, not_before, not_after,
)
})
let certificate_der = crypto_operation(() => certificate.to_der(provider))
{ provider, private_key, certificate, certificate_der, key_type, }
}
///|
pub fn CertificateIdentity::certificate_der(
self : CertificateIdentity,
) -> Bytes {
self.certificate_der
}
///|
pub fn CertificateIdentity::key_type(
self : CertificateIdentity,
) -> CertificateKeyType {
self.key_type
}
///|
fn fingerprint_digest(
algorithm : FingerprintAlgorithm,
) -> @crypto.DigestAlgorithm {
match algorithm {
Sha256 => Sha256
Sha384 => Sha384
Sha512 => Sha512
}
}
///|
pub fn CertificateIdentity::fingerprint(
self : CertificateIdentity,
algorithm? : FingerprintAlgorithm = Sha256,
) -> Fingerprint raise DtlsError {
let value = crypto_operation(() => {
self.certificate.fingerprint(self.provider, fingerprint_digest(algorithm))
})
Fingerprint::new(algorithm~, value~)
}
///|
fn CertificateIdentity::sign(
self : CertificateIdentity,
algorithm : @crypto.SignatureAlgorithm,
data : Bytes,
) -> Bytes raise DtlsError {
crypto_operation(() => self.provider.sign(algorithm, self.private_key, data))
}
///|
const P256_SPKI_PREFIX : Bytes = b"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
///|
fn CertificateIdentity::ephemeral_public_point(
self : CertificateIdentity,
key : @crypto.PrivateKey,
) -> Bytes raise DtlsError {
let der = crypto_operation(() => key.public_der(self.provider))
if der.length() != P256_SPKI_PREFIX.length() + 65 ||
der[0:P256_SPKI_PREFIX.length()] != P256_SPKI_PREFIX ||
der[P256_SPKI_PREFIX.length()] != 4 {
raise HandshakeFailed("OpenSSL returned a non-P-256 public key")
}
der[P256_SPKI_PREFIX.length():].to_owned()
}
///|
fn p256_public_key(
provider : @crypto.Provider,
point : Bytes,
) -> @crypto.PublicKey raise DtlsError {
if point.length() != 65 || point[0] != 4 {
raise InvalidHandshake("P-256 public point must be uncompressed")
}
crypto_operation(() => {
provider.import_public_key(append_bytes(P256_SPKI_PREFIX, point))
})
}
///|
fn verify_peer_certificate(
provider : @crypto.Provider,
certificate_der : Bytes,
expected_fingerprint : Fingerprint,
wall_time : @transport.WallTime,
verify_fingerprint? : Bool = true,
) -> @crypto.PublicKey raise DtlsError {
let certificate = crypto_operation(() => {
provider.import_certificate(certificate_der)
})
let valid_signature = crypto_operation(() => {
certificate.verify_self_signature(provider)
})
let valid_time = crypto_operation(() => {
certificate.is_valid_at(provider, wall_time)
})
if !valid_signature || !valid_time {
raise HandshakeFailed("peer certificate is not valid")
}
let actual = crypto_operation(() => {
certificate.fingerprint(
provider,
fingerprint_digest(expected_fingerprint.algorithm),
)
})
if verify_fingerprint &&
!provider.constant_time_equal(actual, expected_fingerprint.value) {
raise FingerprintMismatch
}
crypto_operation(() => certificate.public_key(provider))
}