Crypto cleanup, doc cleanup, code cleanup (#718)

diff --git a/googleapis_auth/CHANGELOG.md b/googleapis_auth/CHANGELOG.md
index 5d9c72c..fa4fad0 100644
--- a/googleapis_auth/CHANGELOG.md
+++ b/googleapis_auth/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## 2.1.0
 
-- `AuthClientSigningExtension`: Added `sign()` which accepts an optional `serviceAccountCredentials` argument, and `getServiceAccountEmail()` which accepts an optional `email` argument.
+- `AuthClientSigningExtension`: Added `sign()` which accepts an optional
+  `serviceAccountCredentials` argument, and `getServiceAccountEmail()`.
 - `ServiceAccountCredentials`
   - Added parsing for `projectId` and `universeDomain` properties.
   - Added `sign()` method for RSA-SHA256 signing.
@@ -13,7 +14,6 @@
   `ImpersonatedAuthClient` class for service account impersonation via IAM
   Credentials API.
 - Export `RSAPrivateKey` which is exposed by `ServiceAccountCredentials`.
-- Modernized code using pattern matching and switch expressions.
 - Require `google_cloud: ^0.3.0`.
 - Require `meta: ^1.15.0`.
 - Require `sdk: ^3.9.0`.
diff --git a/googleapis_auth/lib/src/crypto/rsa.dart b/googleapis_auth/lib/src/crypto/rsa.dart
index 06bab62..80bb389 100644
--- a/googleapis_auth/lib/src/crypto/rsa.dart
+++ b/googleapis_auth/lib/src/crypto/rsa.dart
@@ -8,7 +8,7 @@
 import 'dart:typed_data';
 
 /// Represents integers obtained while creating a Public/Private key pair.
-class RSAPrivateKey {
+final class RSAPrivateKey {
   /// First prime number.
   final BigInt p;
 
@@ -70,34 +70,16 @@
   }
 
   static BigInt _encryptInteger(RSAPrivateKey key, BigInt x) {
-    // The following is equivalent to `_modPow(x, key.d, key.n) but is much
-    // more efficient. It exploits the fact that we have dmp1/dmq1.
-    var xp = _modPow(x % key.p, key.dmp1, key.p);
-    final xq = _modPow(x % key.q, key.dmq1, key.q);
+    // The following is equivalent to `(x % key.n).modPow(key.d, key.n)` but is
+    // much more efficient. It exploits the fact that we have dmp1/dmq1.
+    var xp = (x % key.p).modPow(key.dmp1, key.p);
+    final xq = (x % key.q).modPow(key.dmq1, key.q);
     while (xp < xq) {
       xp += key.p;
     }
     return ((((xp - xq) * key.coeff) % key.p) * key.q) + xq;
   }
 
-  static BigInt _modPow(BigInt b, BigInt e, BigInt m) {
-    if (e < BigInt.one) {
-      return BigInt.one;
-    }
-    if (b < BigInt.zero || b > m) {
-      b = b % m;
-    }
-    var r = BigInt.one;
-    while (e > BigInt.zero) {
-      if ((e & BigInt.one) > BigInt.zero) {
-        r = (r * b) % m;
-      }
-      e >>= 1;
-      b = (b * b) % m;
-    }
-    return r;
-  }
-
   static BigInt bytes2BigInt(List<int> bytes) {
     var number = BigInt.zero;
     for (var i = 0; i < bytes.length; i++) {
diff --git a/googleapis_auth/lib/src/crypto/rsa_sign.dart b/googleapis_auth/lib/src/crypto/rsa_sign.dart
index 3c945b6..4fb4c9b 100644
--- a/googleapis_auth/lib/src/crypto/rsa_sign.dart
+++ b/googleapis_auth/lib/src/crypto/rsa_sign.dart
@@ -6,18 +6,34 @@
 
 import 'package:crypto/crypto.dart';
 
-import 'asn1.dart';
 import 'rsa.dart';
 
 /// Used for signing messages with a private RSA key.
 ///
 /// The implemented algorithm can be seen in
 /// RFC 3447, Section 9.2 EMSA-PKCS1-v1_5.
-class RS256Signer {
-  // NIST sha-256 OID (2 16 840 1 101 3 4 2 1)
+final class RS256Signer {
+  // DigestInfo :== SEQUENCE {
+  //     digestAlgorithm AlgorithmIdentifier,
+  //     digest OCTET STRING
+  // }
+  //
+  // Where AlgorithmIdentifier is for the NIST sha-256 OID
+  //   (2 16 840 1 101 3 4 2 1)
   // See a reference for the encoding here:
   // http://msdn.microsoft.com/en-us/library/bb540809%28v=vs.85%29.aspx
-  static const _rsaSha256AlgorithmIdentifier = [
+  // ASN1:
+  // SEQUENCE (length: 0x1f + 2 for sequence + length) [0x30, 0x31]
+  //   SEQUENCE (length: 0x0d + 2 for sequence + length) [0x30, 0x0d]
+  //     OID 2.16.840.1.101.3.4.2.1
+  //       [0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]
+  //     NULL [0x05, 0x00]
+  //   OCTET STRING (length: 0x20 + 2 for octet string + length) [0x04, 0x20]
+  static const _rsaSha256DigestInfoPrefix = [
+    0x30,
+    0x31,
+    0x30,
+    0x0d,
     0x06,
     0x09,
     0x60,
@@ -29,6 +45,10 @@
     0x04,
     0x02,
     0x01,
+    0x05,
+    0x00,
+    0x04,
+    0x20,
   ];
 
   final RSAPrivateKey _rsaKey;
@@ -36,45 +56,22 @@
   RS256Signer(this._rsaKey);
 
   Uint8List sign(List<int> bytes) {
-    final digest = _digestInfo(sha256.convert(bytes).bytes);
+    final digest = sha256.convert(bytes).bytes;
     final modulusLen = (_rsaKey.bitLength + 7) ~/ 8;
 
     final block = Uint8List(modulusLen);
-    final padLength = block.length - digest.length - 3;
+    final padLength =
+        block.length - _rsaSha256DigestInfoPrefix.length - digest.length - 3;
     block[0] = 0x00;
     block[1] = 0x01;
     block.fillRange(2, 2 + padLength, 0xFF);
     block[2 + padLength] = 0x00;
-    block.setRange(2 + padLength + 1, block.length, digest);
-    return RSAAlgorithm.rawSign(_rsaKey, block, modulusLen);
-  }
 
-  static Uint8List _digestInfo(List<int> hash) {
-    // DigestInfo :== SEQUENCE {
-    //     digestAlgorithm AlgorithmIdentifier,
-    //     digest OCTET STRING
-    // }
-    var offset = 0;
-    final digestInfo = Uint8List(
-      2 + 2 + _rsaSha256AlgorithmIdentifier.length + 2 + 2 + hash.length,
-    );
-    {
-      // DigestInfo
-      digestInfo[offset++] = ASN1Parser.sequenceTag;
-      digestInfo[offset++] = digestInfo.length - 2;
-      {
-        // AlgorithmIdentifier.
-        digestInfo[offset++] = ASN1Parser.sequenceTag;
-        digestInfo[offset++] = _rsaSha256AlgorithmIdentifier.length + 2;
-        digestInfo.setAll(offset, _rsaSha256AlgorithmIdentifier);
-        offset += _rsaSha256AlgorithmIdentifier.length;
-        digestInfo[offset++] = ASN1Parser.nullTag;
-        digestInfo[offset++] = 0;
-      }
-      digestInfo[offset++] = ASN1Parser.octetStringTag;
-      digestInfo[offset++] = hash.length;
-      digestInfo.setAll(offset, hash);
-    }
-    return digestInfo;
+    var offset = 2 + padLength + 1;
+    block.setAll(offset, _rsaSha256DigestInfoPrefix);
+    offset += _rsaSha256DigestInfoPrefix.length;
+    block.setAll(offset, digest);
+
+    return RSAAlgorithm.rawSign(_rsaKey, block, modulusLen);
   }
 }
diff --git a/googleapis_auth/lib/src/service_account_credentials.dart b/googleapis_auth/lib/src/service_account_credentials.dart
index a403442..a065467 100644
--- a/googleapis_auth/lib/src/service_account_credentials.dart
+++ b/googleapis_auth/lib/src/service_account_credentials.dart
@@ -59,41 +59,36 @@
     if (json is String) {
       json = jsonDecode(json);
     }
-    if (json is! Map) {
-      throw ArgumentError('json must be a Map or a String encoding a Map.');
-    }
-    // TODO: consider using expressions here
-    final identifier = json['client_id'] as String?;
-    final privateKey = json['private_key'] as String?;
-    final email = json['client_email'] as String?;
-    final type = json['type'];
-    final projectId = json['project_id'] as String?;
-    final universeDomain =
-        json['universe_domain'] as String? ?? defaultUniverseDomain;
-
-    if (type != 'service_account') {
-      throw ArgumentError(
-        'The given credentials are not of type '
-        'service_account (was: $type).',
-      );
-    }
-
-    if (identifier == null || privateKey == null || email == null) {
-      throw ArgumentError(
+    return switch (json) {
+      final Map map &&
+          {
+            'type': 'service_account',
+            'client_id': final String identifier,
+            'private_key': final String privateKey,
+            'client_email': final String email,
+          } =>
+        ServiceAccountCredentials(
+          email,
+          ClientId(identifier),
+          privateKey,
+          impersonatedUser: impersonatedUser,
+          projectId: map['project_id'] as String?,
+          universeDomain:
+              map['universe_domain'] as String? ?? defaultUniverseDomain,
+        ),
+      final Map map when map['type'] != 'service_account' =>
+        throw ArgumentError(
+          'The given credentials are not of type '
+          'service_account (was: ${map['type']}).',
+        ),
+      Map _ => throw ArgumentError(
         'The given credentials do not contain all the '
         'fields: client_id, private_key and client_email.',
-      );
-    }
-
-    final clientId = ClientId(identifier);
-    return ServiceAccountCredentials(
-      email,
-      clientId,
-      privateKey,
-      impersonatedUser: impersonatedUser,
-      projectId: projectId,
-      universeDomain: universeDomain,
-    );
+      ),
+      _ => throw ArgumentError(
+        'json must be a Map or a String encoding a Map.',
+      ),
+    };
   }
 
   /// Creates a new [ServiceAccountCredentials].
diff --git a/googleapis_auth/test/crypto/rsa_sign_test.dart b/googleapis_auth/test/crypto/rsa_sign_test.dart
index 9e0d135..f2d61c4 100644
--- a/googleapis_auth/test/crypto/rsa_sign_test.dart
+++ b/googleapis_auth/test/crypto/rsa_sign_test.dart
@@ -79,4 +79,11 @@
       184, 21, 130, 61, 138, 27, 226, 70, 119, 195, 223, 180, 121,
     ]);
   });
+
+  test('different messages generate different signatures', () {
+    final sig1 = signer.sign(ascii.encode('test message A'));
+    final sig2 = signer.sign(ascii.encode('test message B'));
+
+    expect(sig1, isNot(equals(sig2)));
+  });
 }