-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update signing logic for correct custom token format #23
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
import 'dart:convert'; | ||
import 'dart:typed_data'; | ||
|
||
import 'package:crypto/crypto.dart'; | ||
import 'package:googleapis_auth/googleapis_auth.dart' as auth; | ||
import 'package:http/http.dart' as http; | ||
import 'package:meta/meta.dart'; | ||
import 'package:pointycastle/pointycastle.dart'; | ||
|
||
import '../../dart_firebase_admin.dart'; | ||
|
||
|
@@ -107,11 +107,33 @@ class _ServiceAccountSigner implements CryptoSigner { | |
|
||
@override | ||
Future<Uint8List> sign(Uint8List buffer) async { | ||
final key = utf8.encode(credential.privateKey); | ||
final hmac = Hmac(sha256, key); | ||
final digest = hmac.convert(buffer); | ||
final rsaPrivateKey = _parsePrivateKeyFromPem(); | ||
final signer = Signer('SHA-256/RSA') | ||
..init(true, PrivateKeyParameter<RSAPrivateKey>(rsaPrivateKey)); | ||
final signature = signer.generateSignature(buffer) as RSASignature; | ||
return signature.bytes; | ||
} | ||
|
||
RSAPrivateKey _parsePrivateKeyFromPem() { | ||
final privateKeyString = credential.privateKey | ||
.replaceAll('-----BEGIN PRIVATE KEY-----', '') | ||
.replaceAll('-----END PRIVATE KEY-----', '') | ||
.replaceAll('\n', ''); | ||
final privateKeyDER = base64Decode(privateKeyString); | ||
|
||
final asn1Parser = ASN1Parser(Uint8List.fromList(privateKeyDER)); | ||
final topLevelSequence = asn1Parser.nextObject() as ASN1Sequence; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Too many casts and We'd also need tests for when those values are invalid There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! I'd like you to give some advice about this point! I'm thinking to improve the code like the following to reduce crypto_signer.dartRSAPrivateKey _parsePrivateKeyFromPem() {
final privateKeyString = credential.privateKey
.replaceAll('-----BEGIN PRIVATE KEY-----', '')
.replaceAll('-----END PRIVATE KEY-----', '')
.replaceAll('\n', '');
final privateKeyDER = base64Decode(privateKeyString);
final asn1Parser = ASN1Parser(Uint8List.fromList(privateKeyDER));
final topLevelSequence = asn1Parser.nextObject();
if (topLevelSequence is! ASN1Sequence) {
throw const FormatException('Invalid ASN1 format for private key');
}
final privateKeyOctet = topLevelSequence.elements?[2];
if (privateKeyOctet is! ASN1OctetString) {
throw const FormatException('Invalid ASN1 format for private key');
}
final privateKeyParser = ASN1Parser(privateKeyOctet.valueBytes);
final privateKeySequence = privateKeyParser.nextObject();
if (privateKeySequence is! ASN1Sequence) {
throw const FormatException('Invalid ASN1 format for private key');
}
final modulus = privateKeySequence.elements?[1];
final exponent = privateKeySequence.elements?[3];
final p = privateKeySequence.elements?[4];
final q = privateKeySequence.elements?[5];
if (modulus is! ASN1Integer ||
exponent is! ASN1Integer ||
p is! ASN1Integer ||
q is! ASN1Integer) {
throw const FormatException('Invalid ASN1 format for private key');
}
return RSAPrivateKey(
modulus.integer!,
exponent.integer!,
p.integer,
q.integer,
);
} and tried to write the unit test for success case like the following: crypto_signer_test.darttest('parsePrivateKeyFromPem should parse valid private key', () {
final credential = Credential.fromServiceAccountParams(
clientId: 'id',
privateKey: _fakeRSAKey,
email: 'email',
);
final app = FirebaseAdminApp.initializeApp('project-id', credential);
final signer = cryptoSignerFromApp(app);
expect(signer, isA<CryptoSigner>());
expect(signer.algorithm, 'RS256');
final buffer = Uint8List.fromList(List.generate(32, (i) => i));
expect(() async => signer.sign(buffer), returnsNormally);
}); However, when I try to write test to confirm the code throwing the I think I appreciate if you have any ideas about it! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One way to do it is to make Make sure to make |
||
final privateKeyOctet = topLevelSequence.elements![2] as ASN1OctetString; | ||
|
||
final privateKeyParser = ASN1Parser(privateKeyOctet.valueBytes); | ||
final privatekeySequence = privateKeyParser.nextObject() as ASN1Sequence; | ||
|
||
final modulus = (privatekeySequence.elements![1] as ASN1Integer).integer!; | ||
final exponent = (privatekeySequence.elements![3] as ASN1Integer).integer!; | ||
final p = (privatekeySequence.elements![4] as ASN1Integer).integer; | ||
final q = (privatekeySequence.elements![5] as ASN1Integer).integer; | ||
|
||
return Uint8List.fromList(digest.bytes); | ||
return RSAPrivateKey(modulus, exponent, p, q); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a suggestion: https://github.com/jonasroussel/dart_jsonwebtoken comes with a convenient
RSAPrivateKey(String pem)
constructor. Could this be used instead of all the ASN1 code?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to do the trick (it passes the test in
crypto_signer_test.dart
):