Skip to content
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 to latest RSA did:key #30

Closed
expede opened this issue Dec 7, 2021 · 6 comments
Closed

Update to latest RSA did:key #30

expede opened this issue Dec 7, 2021 · 6 comments

Comments

@expede
Copy link
Member

expede commented Dec 7, 2021

New format RSA PK multicodec made its way over to the did:key spec: https://github.com/w3c-ccg/did-method-key/blob/main/index.html#L362-L381

@matheus23
Copy link
Member

I'm trying to understand this whole thing better:

So, there exist multiple ways to byte-encode RSA public keys. Two popular ways are called RSAPublicKey and SubjectPublicKeyInfo. The latter wraps the former with some additional data. Here's the difference (as dumped by dumpasn1) from a transitively dependent issue:

RSAPublicKey:

  0 266: SEQUENCE {
  4 257:   INTEGER
       :     00 B1 B5 FC D8 D4 D5 E8 8C A5 C4 28 7B 31 F5 78
       :     86 5C AF 6A 78 82 6A 3B 8F F7 B1 B2 3A A4 AF 4E
       :     6A 04 74 13 9F 94 5B D9 D3 A9 11 FF D0 FA 72 DB
       :     78 E4 59 3A 86 C9 1F 9D A8 36 18 6E BD 34 02 D5
       :     1F 6A 38 44 24 7A B5 08 8B 46 92 9C 31 1B 43 60
       :     4F ED 84 49 9F 06 F0 F3 9B F5 55 90 E2 18 7F 1B
       :     BE 22 9B A8 F4 9F D2 E5 70 CD 0E 37 33 C7 6B FE
       :     DE 80 E1 E7 E0 88 1E 45 38 EA 1B C5 78 2B E9 51
       :     2F 8B F1 32 52 6E 05 F4 92 35 64 FE 6E 1E 21 BF
       :     70 87 AD EB B6 60 FA 53 3F CA 06 D0 A8 E6 69 91
       :     E1 31 2D 70 B6 4A 61 DA 8C A7 7C 02 8C DF 93 96
       :     01 E2 CF FE 28 55 75 9B 1D 47 9F 08 47 BC AC 59
       :     D7 D6 24 27 A4 31 D4 E3 DF D2 69 74 28 B9 DD 63
       :     09 A7 B1 16 7D B9 9F E0 1A F1 FF 1B 73 95 66 FB
       :     94 9E 47 08 9D 1D AE 84 AE F1 21 4E F7 F5 77 3E
       :     86 A0 D3 E9 DA 24 6F 9B EC 09 D6 0C 70 58 91 18
       :     3D
265   3:   INTEGER 65537
       :   }

SubjectPublicKeyInfo:

  0 290: SEQUENCE {
  4  13:   SEQUENCE {
  6   9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 17   0:     NULL
       :     }
 19 271:   BIT STRING, encapsulates {
 24 266:     SEQUENCE {
 28 257:       INTEGER
       :         00 B1 B5 FC D8 D4 D5 E8 8C A5 C4 28 7B 31 F5 78
       :         86 5C AF 6A 78 82 6A 3B 8F F7 B1 B2 3A A4 AF 4E
       :         6A 04 74 13 9F 94 5B D9 D3 A9 11 FF D0 FA 72 DB
       :         78 E4 59 3A 86 C9 1F 9D A8 36 18 6E BD 34 02 D5
       :         1F 6A 38 44 24 7A B5 08 8B 46 92 9C 31 1B 43 60
       :         4F ED 84 49 9F 06 F0 F3 9B F5 55 90 E2 18 7F 1B
       :         BE 22 9B A8 F4 9F D2 E5 70 CD 0E 37 33 C7 6B FE
       :         DE 80 E1 E7 E0 88 1E 45 38 EA 1B C5 78 2B E9 51
       :         2F 8B F1 32 52 6E 05 F4 92 35 64 FE 6E 1E 21 BF
       :         70 87 AD EB B6 60 FA 53 3F CA 06 D0 A8 E6 69 91
       :         E1 31 2D 70 B6 4A 61 DA 8C A7 7C 02 8C DF 93 96
       :         01 E2 CF FE 28 55 75 9B 1D 47 9F 08 47 BC AC 59
       :         D7 D6 24 27 A4 31 D4 E3 DF D2 69 74 28 B9 DD 63
       :         09 A7 B1 16 7D B9 9F E0 1A F1 FF 1B 73 95 66 FB
       :         94 9E 47 08 9D 1D AE 84 AE F1 21 4E F7 F5 77 3E
       :         86 A0 D3 E9 DA 24 6F 9B EC 09 D6 0C 70 58 91 18
       :         3D
289   3:       INTEGER 65537
       :       }
       :     }
       :   }

To me it makes a lot of sense to use RSAPublicKey instead of SubjectPublicKeyInfo. However, it looks like the webcrypto API only supports the "spki" (which - I now get - stands for subject public key info!) key importing.
I've tried to pass "raw" to the key import for webcrypto, but that doesn't work:

> rsaRaw = u.fromString("MIIBCgKCAQEAsbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR/9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE/thEmfBvDzm/VVkOIYfxu+Ipuo9J/S5XDNDjczx2v+3oDh5+CIHkU46hvFeCvpUS+L8TJSbgX0kjVk/m4eIb9wh63rtmD6Uz/KBtCo5mmR4TEtcLZKYdqMp3wCjN+TlgHiz/4oVXWbHUefCEe8rFnX1iQnpDHU49/SaXQoud1jCaexFn25n+Aa8f8bc5Vm+5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm+wJ1gxwWJEYPQIDAQAB", "base64")
> const { webcrypto } = require("one-webcrypto")
// importing using "raw" and RSA doesn't work in general
> await webcrypto.subtle.importKey("raw", rsaRaw, { name: "RSA-OAEP", hash: "SHA-256" }, true, ["encrypt"])
Uncaught:
DOMException [NotSupportedError]: Unable to import RSA key with format raw
    at __node_internal_ (node:internal/util:477:10)
    at Object.rsaImportKey (node:internal/crypto/rsa:324:13)
    at SubtleCrypto.importKey (node:internal/crypto/webcrypto:479:10)
    at REPL153:1:47
// and importing this RSAPublicKey using SPKI obviously also doesn't work
> await webcrypto.subtle.importKey("spki", rsaRaw, { name: "RSA-OAEP", hash: "SHA-256" }, true, ["encrypt"])
Uncaught Error: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
    at createPublicKey (node:internal/crypto/keys:595:12)
    at Object.rsaImportKey (node:internal/crypto/rsa:261:19)
    at SubtleCrypto.importKey (node:internal/crypto/webcrypto:479:10)
    at REPL154:1:47 {
  opensslErrorStack: [
    'error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error',
    'error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error'
  ],
  library: 'asn1 encoding routines',
  function: 'asn1_check_tlen',
  reason: 'wrong tag',
  code: 'ERR_OSSL_ASN1_WRONG_TAG'
}

Importing raw keys also doesn't work in the browser.


Maybe the fix is to take the RSAPublicKey encoded RSA keys, wrap them with all the bytes needed for them to become SubjectPublicKeyInfo keys and use that to import them into WebCrypto?
And the reverse: When we generate an RSA key using the WebCrypto API, strip the bytes from SubjectPublicKeyInfo away so we get an RSAPublicKey?

I'll probably try that to fix this issue.


I also read something about the RSAPublicKey format being like the pkcs1 format (at least in nodejs), but that doesn't seem to work in the WebCrypto API which only supports pkcs8, which seems to be only for private RSA keys, not public ones.

@expede
Copy link
Member Author

expede commented Jan 6, 2022

TL;DR the W3C spec is going to use ASN.1/DER

@matheus23

I'm trying to understand this whole thing better

Thanks for digging in!

Yup this is confusing in the main doc at present. There's a whole conversation about this across the multiformats, W3C DID repos, and I think Twitter and Discord. It's going to be specified as ASN.1/DER (which I believe is RSAPublicKey). If we get that in another format from the WebCrypto API, we're going to need to convert it, but I'd be surprised if that was the case given how prevalent ASN.1 is (or was).

From Multiformats (updated since the initial comment): "RSA public key. DER-encoded ASN.1 type RSAPublicKey according to IETF RFC 8017 (PKCS #1)"

Maybe the fix is to take the RSAPublicKey encoded RSA keys, wrap them with all the bytes needed for them to become SubjectPublicKeyInfo keys and use that to import them into WebCrypto?

Yup. Not sure if this is helpful: https://www.npmjs.com/package/asn1.js

Here's a few relevant links:

@matheus23
Copy link
Member

Alright. After some more investigation it looks like SPKIs are just 24 extra bytes of header info prefixed to RSAPublicKeys.

That means going back and forth is really easy.
These are the 24 bytes:

Uint8Array(24) [
  48, 130,  1,  34,  48, 13,  6, 9,
  42, 134, 72, 134, 247, 13,  1, 1,
   1,   5,  0,   3, 130,  1, 15, 0
]

These represent exactly this ASN1 DER structure:

  0 290: SEQUENCE {
  4  13:   SEQUENCE {
  6   9:     OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
 17   0:     NULL
       :     }
 19 271:   BIT STRING, encapsulates {

(In my above comment you can see how the contained RSAPublicKey starts at byte 24)

I'm pretty sure that they're always the same, no matter what RSAPublicKey you have at hand. And since it's always the same, I'm also pretty sure that it's always 24 bytes.
We can hardcode them and use that to import RSAPublicKeys into WebCrypto, even though WebCrypto only supports SPKIs.

@matheus23
Copy link
Member

What do we do with the old RSA prefix bytes?

Technically, those bytes now "exist". We could try to specify them as spki in the multiformats table?

@expede
Copy link
Member Author

expede commented Jan 9, 2022

@matheus23 Do you mean The old version that we used? We're going to maintain that as a backwards compatibility for our users, but IIRC we used the "raw bytes" section from the table. This means that our parser needs to be able to switch on both of these, and if it gets the raw bytes + length-indicating bytes, it should try to match the signature against RSA. This is what I did in my initial [never to be merged] PR at updating the byte sequence before the further changes to the spec.

No one else uses our old DID format, and no one should. Consider it a quirk of our system from the very early days that we're maintaining for those early users because it's very low effort to do so, but that will be phased out eventually.

@matheus23
Copy link
Member

Fixed by #42

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants