Search code examples
ipfs

Is it possible to derive an IPNS name from an IPFS keypair without publishing?


We would like to allow users to restore their databases from an IPNS pointer based on IPFS key names that they maintain. However, due to there being only two endpoints for IPNS name/resolve and name/publish, it doesnt appear that one can derive the IPNS name from a key without re-pointing the IPNS name. Is it possible?

I have found that the keys all exist in the keystore fodler inside of IPFS_HOME, but I'm not sure about the format of these files. The comments here suggest that they are in protobuf format, but I'm not sure how to extract the data meaningfully from there.

For example, using --decode-raw gives me this:

root@ip-172-31-11-196:~# protoc --decode_raw $IPFS_HOME/keystore/keypairfile
1: 0
2: "0\202\004\243\002\001\000\002\202\001\001\000\333\246v\\+s\350\321`8Y\224Y\353I\267h\254\257\334\356Q\276W\017ZA\357\327\331\t\304?\327Y\312tP\201\377\235^\014\376\245Q1*[n\004Lh\377\256F\350\235yr\231\233\353\237D~\321\254j\252\273I[\245rv\3046\3441WO\257\252+\273|\216\207\204z<~2K\265\362\224\016\246\334+\373JE\326\371\n\032,n\025\266\335\374\244\202\242\364>\033\275\345l\327\350\353\\\206~*h\237\223\321\337\037P\212\227\204\014W;\205\314pE\256\207\200\t\3275\256\234\'K\177E\253\355c\225\340\251\370\222\371\247\366\353\0140}\312J\254\267+\007\323\366\357\350\244>[\203\031Z\217\311\'\324_o\017\307\262\034\234F4\267\014\343\221NM\221\214\230\034D*-Pa\315%\2513\016H\311\375\004\345\243Q2\277\007\330\217\363\306\272{\357\237\252\204\332\301q\276\000\362\032\212\212\202I\031\002\003\001\000\001\002\202\001\000\002\003\247\3106\231\314\203\307\007\035C0\003\351\\&8\2270F{7l\242\366g\356d#Xd\225UK<\201\016\217\362\241%\320\246\261+jq\001\377\243Ht\032\014&\030R\001`\034\252\202a\246\206\'4\026\222T?Z\370\314p\354a\270{\377Y\372\271k\307?`+;\372\306\375=F\326wP\006F_t\203\314m\221\210\035 \232Q\264\037F/\337\356V\3028\243\226}9\037M\302\202\305Z%\355\007\010\337\316L\276\203%\233\031\216\010P\235\t\307\372\203\362\206\265\327\022W\032\327e\213k?\001\314\324k\022\303\306W\25798\350\240\357\334\311Ft\275\204\342\207^\326k-)]eD\341\007\372\377-J\261\\cJh\246\212\010\025\t\252Q\\\340\221\330\277q\t6\210${\222\225y\260\266\r\204\014P\257\262DR\307\001\216\250$\212\220\362xp\311\022\321\357*\2309\t\232\342\260\001\002\201\201\000\371p|\305\276\276*\253%\244\276\360i\203Y\002\2217$\372g\017WT!C\225\362n\377`\r\t\203(q\353\323K\202\356X\273B\341\037p\t\017\354|+_\317\313k\210\335\000Fr3pqW\001>G\243\231y\373p7\335\201K\362>\333]Od\365\255\360\373\214\245\237\267R\354GA\366\016W\371@*s05`>}\217\361\363PU2]\225\226]=\356n\005\236P\307w\353\342\001\002\201\201\000\341mfm9y\266\343\251>\314\007C\310\324\210\302\230\210\237\353\021|W\330\035w\223~\'SD9r\272\377;)\250\336)[X<XH\021a\024\3108o\261\214\007\240\301\326\222,\260e\001m\370\320\032\354\246u\231\177\337\227\371\214]\242~{o\020h@\220(-\337FF\340\221X\340\001*\t\376\350\343\345\345\224\336\261g>\276\215\252\335H\233\227\210\256\315\302\372(\376\221\265c\260\3537\031\002\201\200O9A\367\320h7\307\031\362\244NYD\305m\202O\300g\343y\304\343\314\230\331\264!:\354\367\327\020\2304\356\220\262\210\010I\230XZ\206\020\240`5]\016\255\244\242\330)\244\377\244`;$8kH\322\316\020\020\373\34475\027\036,\317\350\324\345c\005\016\336\313\016*\022\244\222\246<\2639(\374OF\263\361\207\232E)\247O[\373\235\252\343\024W\022\336\252\010\264\204\2576$3\346K\276\001\002\201\200\023\347\320\"\226\357\253y\240\351=\244\352\224bH\r>\340\331\226->\030\227\251\312tH\260zF\314\367\327\221P\r\026\257?a\244\201\367\235\255\2030\r\232\006*\334]\224\021i\274\020\234&\337 q\327\026y\215\035WG\226[\332\032M\356ZR\325\364\321\357\331\212\342\272\023\177\220\266\344\355*\315&\202\316\327\310\346#\346\".&\2716\323>\244\371\260@\316\206\266\317\326,\334z\362\351\235k\211\002\201\201\000\311\231\273\205\340\260\0360\252\236G\221\305\201!\305\013\013\\\217N\332\004\364>\314\nP\016\375-\240b\\\246=\264C\t\357\362\303xC\327P\353\034\224\023Xn\204\360\300A3\001N\262\3233r\004\346v\300v\"g\315\034\275\001L;4,\224\302\376\372\324\037Z\232\245)2\364\034\177\307\341\0045(\326\360\3311\265\002\231\265Y\014Cb%\202uB\033\313\177-kr\345\026\247\357\237\247+\310\272"

Which appears to be two binary items, but still nothing I can quite extract the IPNS name from... or is it?


Solution

  • Here's a working example in Go of how to read the RSA key pair and base58 encode the public key to get the IPNS peer ID:

    package main
    
    import (
        "bufio"
        "bytes"
        "encoding/pem"
        "fmt"
        "log"
    
        "github.com/ipfs/go-ipfs/keystore"
        b58 "github.com/mr-tron/base58/base58"
        mh "github.com/multiformats/go-multihash"
    )
    
    func main() {
        ks, err := keystore.NewFSKeystore("/Users/username/.ipfs/keystore/")
        if err != nil {
            log.Fatal(err)
        }
    
        priv, err := ks.Get("mykey")
        if err != nil {
            log.Fatal(err)
        }
    
        privBytes, err := priv.Bytes()
        if err != nil {
            log.Fatal(err)
        }
    
        var privPEM bytes.Buffer
        privWriter := bufio.NewWriter(&privPEM)
        pem.Encode(privWriter, &pem.Block{
            Type:  "RSA PRIVATE KEY",
            Bytes: privBytes,
        })
        privWriter.Flush()
    
        pub := priv.GetPublic()
        var pubPEM bytes.Buffer
        pubBytes, err := pub.Bytes()
        pubWriter := bufio.NewWriter(&pubPEM)
        pem.Encode(pubWriter, &pem.Block{
            Type:  "RSA PUBLIC KEY",
            Bytes: pubBytes,
        })
        pubWriter.Flush()
    
        privPEMString := string(privPEM.Bytes())
        pubPEMString := string(pubPEM.Bytes())
    
        var alg uint64 = mh.SHA2_256
        maxInlineKeyLength := 42
        if len(pubBytes) <= maxInlineKeyLength {
            alg = mh.ID
        }
        hash, _ := mh.Sum(pubBytes, alg, -1)
        peerID := b58.Encode(hash)
    
        fmt.Println(privPEMString)
        fmt.Println(pubPEMString)
        fmt.Println("IPNS:", peerID)
    }
    

    Output:

    -----BEGIN RSA PRIVATE KEY-----
    CAASpwkwggSjAgEAAoIBAQDjwkdyAvwfL6zIr2O6MF2ipNd/pkkV+5+9wOyvAL9I
    HY4WRMmmLBGiBTj1Kln79ttChegxy/XME7ipzt2KXq7qaMT7ZiscB0pPpLCpViue
    GxY90VbYmTeUrtvFJ+fJ5k4InuYtxJpwIpK0zkTZEQbq7mjOOl0VaIzH4iBjKKDq
    uYXxX5JSz5/9I2a4d5YvOoci703krFEAGw4FsnUR+NU0wKE2A66+t+xml3E6D/Oo
    OowSzLk2o0vZOFqNG4/tas9HGKYO8ofmFYMhbw5j/p3lZCHhPdklZZ4nlrEZIxPT
    lwdeY3qBG+7WyzctLbJLerg8cvwOJ5m46uQ4f0j4mYf9AgMBAAECggEAQPhftxdA
    4oiQCmN12FwJqebKDoDZ4mp4BAIvwjA9YI4FxTZ0K3Hwyb4hpFYjeyvNGinmtaXQ
    BRovGEmJivo9fWLLpkdbztAJk7SQLI4HPT/O1W2ND5aNc116FcCvbznCUtU5Jh8q
    SwihvvAEVcN0rLm7rfCaMwwy9KX9xSuG0830suMmVJ31W0evzRYHtnsvmDC9rdrA
    TOy2HZdbL5Bty+nSwOlD608hCPWG6jCH5/BpEA3nCGX6KGH4yqFjaWGRA54cGGsh
    bcKP1Hsdk0zle+Epl04yR1h2XCuwfmM3yegZuKjG5dfF51kV0yq2kND4F47D5azp
    QJEhYHHOi6dvhQKBgQDwhZVl0W1rZjUvzkf/mZ2uC7aL64Rg1DFWNpSc05NK4IBS
    eAf17HCCxLHCZnR0r8+CC0bOR+0xFT24RKcDyiPzpUo90LKHV3CXZEeFEWwfCg6N
    RUfKMVLu9Vf5z7U4V2lOkojE7pWLt/ZMEAxXByQPlNyARfiRI8HPsPgrTlJvKwKB
    gQDyam+oJbAfLvEFQ+h0B1ov3/3BEzV3GRkRz/IHTzQvHKyrN8zn/NZzQfrv3GR8
    5W9BumpdfPkV5kCJb00fjS7AgLGWGHBsqzyDdliZdiQ5h5b15SflDrzfCF6c23lp
    iizEtoNWTFYpgp3MuXYtfvr+6p6VdwZkZ5xTUebK4bIRdwKBgErJnqysNBPDEiKt
    R6HoiCkIJ1jWgLEDCdw+2HLzLseHix8Zh5AmVs2yj3tdFDT8Pc+35epaXxx1+F0F
    q9D317n42V7jN7/xpmbMnZh41F/KZr/ynOH3+EupKhPZTAYa1/nAgpqJfSKWrxTz
    oIKnC8V0iiOwnhuzPJ5x7pi4n9VFAoGBAMV7OdzkXvb4WbatXJfxSWJI+kKosFyG
    oKqnGYck2eErXerZuV6f1d/tN0zh4SbyDdGg2HeykIRrn9WWS2DRte1yqbkZzbRp
    RxHdfk3+NYJ8V9mXxglPGUQkYFcuYFk/DDtwZ8wMwgBs/LpDt+dWU4kJfwlJ/nYb
    BfnGddp/RH3/AoGAYxXvoxuhGzMXvDLNJ0SNuqNUfZ/F/ltMAcPmhtJkTo+RpZlm
    eBvXrduLemNPYcNxTNmuRMgA4kVHOlu+/s7VQ4sRcNp7Fgau3+aqkE/9pkV3wJnf
    xTlPlC4y+VeMKqBczpIXBD42mQMSwe9x/chOVQ+oebqB6Nvyk8RbcI9fJEs=
    -----END RSA PRIVATE KEY-----
    
    -----BEGIN RSA PUBLIC KEY-----
    CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjwkdyAvwfL6zI
    r2O6MF2ipNd/pkkV+5+9wOyvAL9IHY4WRMmmLBGiBTj1Kln79ttChegxy/XME7ip
    zt2KXq7qaMT7ZiscB0pPpLCpViueGxY90VbYmTeUrtvFJ+fJ5k4InuYtxJpwIpK0
    zkTZEQbq7mjOOl0VaIzH4iBjKKDquYXxX5JSz5/9I2a4d5YvOoci703krFEAGw4F
    snUR+NU0wKE2A66+t+xml3E6D/OoOowSzLk2o0vZOFqNG4/tas9HGKYO8ofmFYMh
    bw5j/p3lZCHhPdklZZ4nlrEZIxPTlwdeY3qBG+7WyzctLbJLerg8cvwOJ5m46uQ4
    f0j4mYf9AgMBAAE=
    -----END RSA PUBLIC KEY-----
    
    IPNS: QmTU9rhcwQM6refNwa64oqibG6C2mmFLhhxDBUjWDBtRa6
    

    The key was generated by doing:

    ipfs key gen mykey --type=rsa --size=2048
    

    It'll be a very similar process for other languages such as Node.js